Andy Kelk


Moving from Apache to Nginx

As well as hosting sites on the Windows/IIS/.Net platform, we also have quite a few sites that run on the LAMP (Linux/Apache/MySQL/PHP) stack. Although Apache had been tweaked and tuned, we had periodic issues with unexpected load hitting Apache and causing it to slow down or (more often) grind to a halt.

Enter Nginx – the high concurrency webserver that now runs 8.5% of the world’s websites. For running static websites, Nginx seems a no-brainer – it’s dead simple to set up with fewer lines of configuration than Apache and performs admirably under load. Of course, most of the websites we were looking to move had dynamic content to some extent (some WordPress sites; some built with Yii; at least one vanilla, hand-rolled PHP site).

There’s multiple ways to spin this one – from using Nginx as a proxy through to running PHP-FPM (FastCGI Process Manager). In the end, we’ve settled on a set-up using Nginx, spawn-fcgi and PHP. Running on CentOS 5.7, it was remarkably simple to get Nginx up and running – here’s what we did:

  1. Install Nginx
    Installing Nginx is easy if you have the EPEL repository activated. It’s just a simple:

    yum install nginx

    The Nginx config files are located in /etc/nginx

  2. Install spawn-fcgi
    spawn-fcgi is part of the lighttpd project; it runs as a daemon, listening on a port (default is 9000) for connections from the web server. It then manages the spawning of FastCGI processes (PHP in our case). It’s also in the EPEL repos, so go ahead and install it:

    yum install spawn-fcgi
  3. Configure and start spawn-fcgi
    Next up is to configure spawn-fcgi. This is done by editing /etc/sysconfig/spawn-fcgi and providing an OPTIONS environment variable:

    OPTIONS="-u apache -g apache -p 9000 -C 5 -P /var/run/spawn-fcgi.pid -- /usr/bin/php-cgi"

    -u is the user to run as, -g the group to run as, -p is the port to listen on, -C is the number of child processes to spawn for PHP and -P is the pidfile.
    We are running as apache:apache, just to avoid changing any existing file permissions when migrating from Apache. If this is a fresh setup, you might as well run as the nginx user.
    Once that’s done, you can add spawn-fcgi to be started on boot and start it up:

    chkconfig spawn-fcgi on
    service spawn-fcgi start
  4. Configure Nginx
    Here comes the science bit. This step was definitely the most labour-intensive and required a fair bit of manual work, lots of learning and some trial-and-error. We had 21 VirtualHost blocks in our Apache config on one server – each ever-so-slightly different. The basic lessons we learned here:

    • We put each server{} block into its own file in /etc/nginx/conf.d to aid navigation through the files
    • SSL is pretty easy – just follow the example in ssl.conf and paste in the right locations for your SSL certificate/key
    • Simple rewrite rules are quite simple to convert across from mod_rewrite but be sure to put them in a location {} block if appropriate and beware of  using “if”
    • Related to the above, you can use try_files to get WordPress fancy URLs working:
      location / {
          try_files $uri $uri/ /index.php?q=$uri&$args;
      }
    • You can specify one server as a default if a hostname comes through that is not matched:
      listen 80 default_server;
    • Prefixing a hostname with a . is really useful. It matches the exact hostname or any name ending with that value (for example .andykelk will match andykelk.net, www.andykelk.net, blog.andykelk.net, etc). You can also activate this behaviour within a map by using the “hostnames” directive (we have a big map which does a 301 redirect from one hostname to another):
      map $http_host  $name {
          hostnames;
          .singaporepropertyforrent.com              www.iproperty.com.sg;
          [snip ....]
      }
      server {
          server_name _;
          listen 80 default_server;
          rewrite  ^   http://$name/   permanent;
      }
  5. Test your setup
    Of course you need to make sure your nginx configuration is all valid, you can do this with:

    service nginx configtest

    You also will be doing all of this on a test environment, of course, and doing some full testing, right?

  6. Stop Apache
    This is an easy one:

    service httpd stop
  7. Start Nginx
    This is pretty easy too:

    service nginx start

UPDATE (2013): Since this article was written, the FPM patches have become more widely adopted as part of the official PHP 5.4 series. We now use php-fpm on CentOS 6 using the PHP rpms from Remi instead of spawn-fcgi.


Leave a Reply

Your email address will not be published. Required fields are marked *

*
*


Archives