We recently had an infrastructure overhaul in order to allow us to expand our hosting options and the uptime and availability of the popular Tumblr Stalkr application.

Apache to Nginx VPS Server Guide

We needed a platform that was easily scalable, had little overhead and was able to cope with a large volume of concurrent connections. Our traffic has been steadily increasing and whilst Apache has been great and is by-far the most well-known, we needed something that would aid performance rather than hinder it. That's why we looked into Nginx and after some consideration, we decided on a setup and got to work migrating everything over.

Step 1 - Install Nginx

Unlike Ubuntu, CentOS doesn't have the repository needed to get Nginx installed so we first need to add the EPEL and Remi repositories:

sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

Now we can install Nginx:

yum install nginx

Potential Problems

When we were setting up Nginx we encountered a wrath of issues on our main VPS that we were using. Mainly, our VPS was running a LAMP stack and cPanel was running lfd which blocked the repository so when we tried to install Nginx we got 'No package nginx found'.

If you encounter any errors, I'd firstly advise making sure the EPEL and Remi repositories are enabled, to do this you can:

vi /etc/yum.repos.d/remi.repo

Now find [remi] and [remi-php55] and make sure they're both set to 'enabled=1', alternatively, you can prepend the yum command below with:

yum --enablerepo=remi,remi-php55

If you're still having issues, you can manually create the CentOS Nginx repository yourself, create the new file:

vi /etc/yum.repos.d/nginx.repo

Add the following contents (if you're not used to vi, you can press "* and i" to get into insert mode:

name=nginx repo

Now try installing Nginx again:

yum --enablerepo=remi,remi-php55 install nginx

Step 2 - Installing PHP-FPM using the Remi Repository

Before we configure Nginx, we want to setup PHP-FPM, its dependencies and then MySQL. In our instance, we already had MySQL installed and the extensions we'll need, so we missed this step but I've included details below.

yum install php-fpm php-common

If you encounter the error message 'No package php-fpm available' I'd advise that you ensure Remi is installed and active with the following command:

yum repolist

If you don't see Remi, repeat the steps above to enable the repository and try again.

Step 3 - Install MySQL and PHP Extensions

With PHP-FPM installed we can now move to setting up any extensions we may want, below is a list of all available extensions and configuring MySQL (though you may already have this installed, if you do, scrap the last two packages on the list below):

yum --enablerepo=remi,remi-php55 install php-pdo php-mysqlnd php-mysql mysql mysql-server

Running the query above will automatically install all our required dependencies. You're able to add any other extensions you feel that you may need:

  • APC (php-pecl-apc) – APC caches and optimizes PHP intermediate code
  • CLI (php-cli) – Command-line interface for PHP
  • GD (php-gd) – A module for PHP applications for using the GD graphics library
  • MBString (php-mbstring) – A module for PHP applications which need multi-byte string handling
  • MCrypt (php-mcrypt) – Standard PHP module provides mcrypt library support
  • Memcache (php-pecl-memcache) – Extension to work with the Memcached caching daemon
  • Memcached (php-pecl-memcached) – Extension to work with the Memcached caching daemon
  • MongoDB (php-pecl-mongo) – PHP MongoDB database driver
  • MySQL (php-mysqlnd) – A module for PHP applications that use MySQL databases
  • PEAR (php-pear) – PHP Extension and Application Repository framework
  • PDO (php-pdo) – A database access abstraction module for PHP applications
  • PostgreSQL (php-pgsql) – A PostgreSQL database module for PHP
  • SQLite (php-sqlite) – Extension for the SQLite v2 embeddable SQL Database Engine
  • XML (php-xml) – A module for PHP applications which use XML

Step 4 - Start PHP-FPM

Great, PHP-FPM is installed so now lets start it so Nginx can use it:

chkconfig php-fpm on
service php-fpm start

Step 5 - Test Nginx Alongside Apache

If you're considering switching over from Apache to Nginx, you'd be crazy to get to this point and start Nginx without configuring it and testing everything works. We want a solution that'll let us test everything thoroughly to ensure there's no downtime. If, like us, your VPS has cPanel, your Apache installation will nicely and automatically build all your VirtualHosts for you. Sadly this isn't the same for Nginx and you'll need to configure them.

To begin with, I'm going to run Nginx on port 81 and when I'm happy with everything, I'll switch Nginx over to 80 and turn off Apache (scary!).

Let's setup Nginx to use port 81 by editing the main configuration file:

vi /etc/nginx/nginx.conf

In our file, look for similar to below and update the number of worker processes and GZIP:

worker_processes  8; # Advised 2 x core CPU
gzip on;

Now lets edit our default configuration:

vi /etc/nginx/conf.d/default.conf

We want to tell Nginx to listen on port 81 whilst we get everything setup:

server {
	listen  81 default_server;
	server_name  _;

Once you've saved, use the commands below to start Nginx.

Step 6 - Fire up Nginx

Enable the Nginx service on startup and then use any of the commands below to start, stop or restart Nginx:

chkconfig nginx on
service nginx start
service nginx stop
service nginx restart
service nginx status
service nginx reload

You should now be able to test your Nginx installation by heading over to your VPS IP address and put a port of 81 on the end.

Step 7 - Setting up Nginx Virtual Hosts

In CentOS 6.5, Nginx stores any vhost's in /etc/nginx/conf.d/virtual.conf. Here you can create new server blocks, or create separate .conf files for each one you want to add.

If you've got access to your Apache hosts, then you're pretty much able to copy and paste parts in without much hassle. However, you'll need to convert all your .htaccess files over (if you have any). We found Winginx to be the most reliable htaccess to Nginx converter.

If you want to route everything to an index.php file, similar to how Wordpress works then you can use the following code, same applies to separate directories:

server {
	listen 81;
	server_name localhost;
	root /usr/share/nginx/website; # This can be any path you wanted your files at
	error_log /var/log/nginx/error.log;
	access_log /var/log/nginx/access.log;
	error_page  404 /404.html;
  	location = /404.html {
       		root /usr/share/nginx/html;
	error_page 500 502 503 504 /50x.html;
	location = /50x.html {
        	root /usr/share/nginx/html;
	location /directory {
		index index.php;
		try_files $uri $uri/ /directory/index.php?$args;
	location / {
		index index.php
		try_files $uri $uri/ index.php;
	location ~ .php$ {
		include fastcgi_params;
		fastcgi_intercept_errors on;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $request_filename; # Or $document_root$fastcgi_script_name

Now save the file, reload Nginx and you should have your first site up and running.

Everything Else that Could Go Wrong

It'd be great if I could say that was potentially the end of the errors.. though you may need to tweak your PHP-FPM configuration to enable it to have more children workers.

To find where PHP-FPM is installed (this was the initial problem for us, it wasn't where we expected), run the command:

whereis php-fpm

Then navigation to the configuration folder and edit its content. When you're editing, you'll see:

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;	will be used.
; RPM: apache Choosed to be able to access some dir as httpd
; Who owns the /website folder, set the permission level
user = nginx
; Who owns the /website folder, set the permission level
; RPM: Keep a group allowed to write in log dir.
group = nginx
pm.max_children = 10

We ran into some permission problems where Nginx was dispatched as 'nginx' whilst PHP-FPM was running as 'nobody' which meant it didn't have write permission to our folders. We setup a separate PHP-FPM pool running on port 9001 and assigned it the user/group we waned, who had the correct permissions on our folders. Sadly, if you've got a lot of vhosts like we did, you'll have to do this for every user. Or just global-assign your web root.

To fix file permissions, you may find the following commands particularly helpful (but don't forget, if you change permissions whilst running Apache, your Apache will give a 403 forbidden). To get around Apache and Nginx issues, we had to create a group that was apache, root and nginx - then assign write permission to that group.

chmod -R 0777 /usr/share/nginx/website
chown -R nginx:nginx /usr/share/nginx/website

Other Helpful Commands

To list members of a group:

getent group nginx

To add a new user to a group:

useradd -G group username

To add an existing user to a group:

usermod -a -G group username

What are the Results?

Moving to Nginx proved rather eventful and in our instance we setup load balancing and Varnish cache in front of everything (we'll get into this next week). Though our server load is under 20 averaging between 250 concurrent tests. Our memory usage is also down to just 12% on average, meaning we could rewrite a lot of our CMS code to optimise it for Memcache, Redis and Varnish.

Overall, I'd strongly recommend Nginx to any budding enthusiast who wants to improve their website performance.

Next time, we'll look into setting up load balancing using Nginx, putting Varnish in front of everything and the server setup using multiple cloud instances with synchronisation between the servers. We'll also look into sticky sessions using Nginx, Memcache optimisation tips and dedicated MySQL database servers.

Ignite your brand, utilise user-generated content no matter where you or your audience are ›