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:

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.


upstream php {
   server unix:/tmp/php-fpm.sock;

Main site config. I compiled it using several articles found and official one from Wordpress.


server {
	listen	80;
        root /home/ameoba32/www/;

        index index.php;

	if ($http_host != "") {
	    rewrite ^$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

Test of static content

$ ab -i -n 500 -c 20

# 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