I quit Apache and moved to Nginx

Now this blog is riding on Nginx+php-fpm setup. You may have heard about quickly growing in popularity Russian made web-server. Several years earlier Nginx had a really narrow appliance on high loaded sites as balancer, front end, static content, etc.. Something too complex to be setup for simple site or blog. Time changed things, community has grown, all sharp corners were smoothed. The PHP itself starting from version 5.3 has out-of-the-box support of FPM (FastCGI Process Manager) to communicate to Nginx in a very efficient manner. Ubuntu PPA repositories has all things already compiled and tons of docs are available on the Internet. At first I though of this more like experiment, but after I tested it for a while and made some performance tests, I was surprised how well it performed on my tiny virtual server. It eats less memory and does more job. I really like Apache web server, I've spend 10 years with it and still consider it the best one for many appliances. But from now on I have one more option to use for my projects. Welcome to continue reading... Actually there was one more reason to try Nginx - memory usage. Some time ago I moved my blog from VPS based on Virtuozzo to the Rackspace cloud based on XEN. This move was more related mostly to money savings. Same characteristic server  (10Gb HDD, 256MB RAM) is twice cheaper on the cloud, less lagging and more productive. But this move revealed that XEN and Virtuozzo virtual machines counts free memory in different ways. I experienced memory shortage on XEN immediately after setup of LAMP stack and WordPress. Default Apache server limits are too optimistic and RAM is quickly filled up with Apache instances of 30Mb in size each. After lowering limits to values 2-3 of server instances situation got better. All this time I knew it could be even better 🙂 Once I found some time to do it, I've done it.

Nginx + php-fpm + WordPress

Setup is very easy, there are tons of blog posts, howtos and official documentation on the Internet. I chose the easiest one and following it have it up and running in half-hour. I did all tests of my LIVE server with Apache running and did not want to crash my current blog activity. So I installed Nginx in parallel on another port (8080) and it went well. After successful tests I just swapped Apache and Nginx

Difference from Apache

In Apache, PHP usually works as module - mod_php, it is loaded into Apache address space and acts like part of it. All requests are handled by Apache instance, either it is static file it is sent directly from HD, in case of .php file, it is passed through mod_php and output is send to the client. Web requests are distributed through all Apache workers in round robin. Since mod_php eats lots of memory, all instances very soon become FAT. Nginx has different approach. By default it serves only static files and does this in a very efficient way. Web requests to dynamic pages, to .php files, are redirected to special FPM daemon which runs this request though one of php-worker process and sends output back to Nginx and further back to the client. FPM is simular to CGI, but more efficient because can handle multiple requests with single process. This difference means, small static files are served very quickly with small memory footprint and dynamic .PHP files are served by several FPM workers.

Installation on Ubuntu 10.04-LTS

Later versions of Ubuntu already have php5-fpm compiled, so no additional repositories are required.
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:nginx/stable
sudo add-apt-repository ppa:nginx/php5
sudo apt-get update
sudo apt-get install nginx php5-fpm

Configure php-fpm

It is a daemon which listens on localhost:9000 or unix socket for PHP requests, processes them balancing through available FPM workers. It also supports creating several pools of workers which may be running under different uid/gid or have different php.ini settings, this advanced usage is not covered here. By default it is configured to have one default worker pool called www and if you are not concerned about performance or memory usage you may leave configuration as default. I did some changes to limit number of processes to fit my little memory. Mine change to /etc/php5/fpm/pool.d/www.conf are:
listen=/tmp/php-fpm.sock
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 2
pm.max_requests = 500

Configure Nginx

How to reach php-fpm. Unix socket seems better option for a local machine. I have this setting in separate config. /etc/nginx/conf.d/php-fpm.conf
upstream php {
   server unix:/tmp/php-fpm.sock;
   #server 127.0.0.1:9000;
}
Main site config. I compiled it using several articles found and official one from WordPress. /etc/nginx/sites-enabled/a32.me
server {
	listen	80;
        server_name a32.me;
        server_name www.a32.me;
        root /home/ameoba32/www/;

        index index.php;

	if ($http_host != "a32.me") {
	    rewrite ^ http://a32.me$request_uri permanent;
	}

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

	# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
	location ~ /\. {
	    deny all;
	    access_log off;
	    log_not_found off;
	}

	location / {
	    # No php is touched for static content
            try_files $uri $uri/ /index.php?$args;
        }

	# Add trailing slash to */wp-admin requests.
	rewrite /wp-admin$ $scheme://$host$uri/ permanent;

        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }

        location ~ \.php$ {
                expires off;
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

                include fastcgi_params;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_intercept_errors on;
                fastcgi_pass php;
        }
}

Launch

/etc/init.d/apache stop
/etc/init.d/php5-fpm start
/etc/init.d/nginx start

Update startup

iIf everything is well, do not forget to switch daemons on system start.
# update-rc.d apache remove
# update-rc.d nginx enable
# update-rc.d php5-fpm enable

What is the difference

OK, I moved, what really has changed ? I did several load tests using ApacheBench (ab) tool, which can generate load on a web server. "-i" switch means to do HEAD requests instead of GET, difference is not to do actual content downloading, just  test pure server performance. Test Apache, after stop and run Nginx and do several tests in a row to get average result. Test of dynamic content, WordPress main page
$ ab -i -n 500 -c 20 http://a32.me/
Test of static content
$ ab -i -n 500 -c 20 http://a32.me/sitemap.xml

Apache vs Nginx on dynamic PHP pages

Apache PHP serving

Requests per second: 6.81 [#/sec] (mean) Time per request: 2935.476 [ms] (mean) Time per request: 146.774 [ms] (mean, across all concurrent requests)

Nginx PHP serving

Requests per second: 14.27 [#/sec] (mean) Time per request: 1401.530 [ms] (mean) Time per request: 70.076 [ms] (mean, across all concurrent requests)

Apache vs Nginx on static content

Apache static content serving

Requests per second: 15.38 [#/sec] (mean) Time per request: 1300.093 [ms] (mean) Time per request: 65.005 [ms] (mean, across all concurrent requests)

Nginx static content serving

Requests per second: 55.84 [#/sec] (mean) Time per request: 358.182 [ms] (mean) Time per request: 17.909 [ms] (mean, across all concurrent requests)

Apache vs Nginx conclusion

On dynamic files I've got it 109% increase in efficiency, on static content 263%. All this by simple changing the web server software. I am very satisfied with simplicity and performance it gives. Nginx setups are also easy to scale using several servers.

Nginx mod_rewrite

One drawback I've found after move is that .htaccess files are not supported in the way Apache does. To overcome this .htaccess configuration has to be converted into Nginx comliant configuration and inserted into directly into Nginx main config. I've found nice converter that can help to do this http://www.anilcetin.com/convert-apache-htaccess-to-nginx/

Links



 							

6 comments

  1. Hey Kostyan!
    Change this google+1 banner to something smaller! What this for? Looks like ad.

    P.S. The article is great! I like nginx so far.

  2. Thanks for taking the time to post this — it got me to try switching from Apache to Nginx on my little vps. So far, so good. Going to add Varnish and APC.

    One item worth noting: In /etc/nginx/sites-enabled/a32.me, there is a comment stating, “#NOTE: You should have “cgi.fix_pathinfo = 0;” in php.ini”. I really think you should add this to the php.ini section so no one overlooks it — apparently nginx + wordpress is vulnerable to hacking without it. See here:

    https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/

  3. .htaccess on WordPress is mostly needed for three things:
    1) Fancy permalinks
    2) Dealing with the extra complexity of WordPress Multisite
    3) Security concerns (i.e. directly blocking attacks and illegitimate access to certain areas)

    The line you have with try_files $uri $uri/ /index.php?$args; should deal with case 1.
    Case 2 is better dealt with the kind of configuration found on the WordPress.org section for Nginx, it adds a bit more complexity.
    Case 3 is a bit more serious, since some popular plugins which harden WordPress’s security will unfortunately only work with Apache. But both WordPress.org and the Nginx Wiki give good examples to protect your website from the worst attacks.

    In any case, I personally use W3 Total Cache to improve performance as well as add some extra security — W3TC is fully Nginx-aware and will generate all appropriate configuration rules. And, on top of that, I put CloudFlare — which will also identify most attacks, and, of course, add further caching of static elements, distributing it among the 14 data centres they own. So the truth is that even the lack of native .htaccess support in Nginx is far less worrying than it used to be a few years ago…

  4. Наталья:При попытке зайти на сайт Улановка.ру с начала декабря выходит вот это сообщение: ForbiddenYou don’t have permission to accses / on this server. ПОЧЕМУ и ЧТО ДЕЛАТЬ?

Leave a Reply

Your email address will not be published.