Old habits die hard. I still love Firefox, and I’m still in love with apache2, but new times are coming. While I was hosting I needed a solution that could handle sites with different users, to log with different user rights and to secure the code from each other as hard as possible. I achieved this with unix users, 0700/0600 dir/files rights and with apache2-mpm-itk, and it did work like charm. Also, I had at least 2GB memory just for apache2, so it’s memory relish and it’s performance was acceptable for the security.

Things are about to change though, I’m moving my own sites to a small VPS with 512MB RAM and 1GB swap (just for sure). Why this low? Because it has to be enough. And to make this sure, I need switching to nginx + php-fpm, combined with APC from apache2 and xcache.

The host is Ubuntu 11.04 (server). Yes, it could be better, more stable, and 10.04, but I need up-to-date packages, I don’t like complying everything myself.

And the real twist: I need this to work with a WordPress 3.0 Network, with domain mapping plugin and all.

Installation

Add the nginx repository.

add-apt-repository ppa:nginx/stable
apt-get update 

Install the packages we need.

Nginx, PHP, PHP-FPM, MySQL – the most needed ones

sudo apt-get install nginx-full php5-fpm php5-cli php5-dev php5-mysql php5-curl php5-gd php5-imagick php5-mcrypt php5-suhosin mysql-server

Suhosin is a security plugin, but it can conflict with lots of application. Be sure I doesn’t ruin yours.

APC

APC is avaliable via PECL, but a developement package is needed for it.

sudo apt-get install php-pear build-essential libpcre3-dev
sudo pecl install apc

Configuration

I remove the comments from the configurations files, so there are just the needs.

Nginx

/etc/nginx/nginx.conf


user www-data;
worker_processes 2;
error_log  /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
  use epoll;
}

http {
  server_names_hash_bucket_size 64;
  sendfile on;
  tcp_nopush  on;
  tcp_nodelay off;
  client_max_body_size 64M;
  types_hash_max_size 8192;

  include /etc/nginx/mime.types;

  default_type text/html;

  log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"' ;

  access_log /var/log/nginx/access.log main;

  include /etc/nginx/gzip_params;

  include /etc/nginx/sites-enabled/*;
}

/etc/nginx/fastcgi_params

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param REDIRECT_STATUS 200;

/etc/nginx/gzip_params


gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_http_version 1.1;
gzip_comp_level 1;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/xml+rss;

/etc/nginx/sites-available/default


server {
    listen  80;
    server_name .domain.com;
    access_log  /var/log/nginx/domain.com.access.log;
    error_log   /var/log/nginx/domain.com.error.log;

    location / {
        root    /var/www/;
        index   index.php index.html index.htm;

        if ( $uri ~ .(ico|gif|jpg|jpeg|png)$  ) {
            expires 30d;
        }

        # WordPress multisite files rule
        rewrite ^.*/files/(.*)$ /wp-includes/ms-files.php?file=$1 last;

        # WordPress rewrite rules
        try_files $uri $uri/ /index.php?q=$uri&$args;

    }

    location ~ .php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /var/www$fastcgi_script_name;
    }

    location ~ /.ht {
        deny  all;
    }

    location /nginx_status {
        stub_status on;
        access_log   off;
    }
}

The real trick is done be the rewrite rules. After hours of searching and thinking about the perfect re-write of the rewrite rules, I’ve found these solutions on a Ruby (!) forum.

PHP

/etc/php5/fpm/pool.d/www.ini

[www]
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
listen.owner = www-data
listen.group = www-data
listen.mode = 0600
user = www-data
group = www-data
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 512
pm.status_path = /status
php_admin_value[open_basedir] = /var/www/
php_admin_value[upload_tmp_dir] = /var/www/tmp/

/etc/php5/fpm/main.conf

pid = /var/run/php5-fpm.pid
error_log = /var/log/php5-fpm.log
log_level = notice
include=/etc/php5/fpm/pool.d/*.conf

Test and conclusion

I did not expect any noticeable difference in performance or in user experience, but I was suprised. While the load and the memory usage lower with a significant value, the speed, the actual, user-based, visitor speed gained at least 300%. And also, there’s virtually no fragmentation in APC, while with apache2… well, it used to be terrible.

Apache2: I’m going to miss you. For 10+ years I have experience with apache and apache2, but there are more important aspects than habits in the world of web servers. The need for apache3 or a really good mpm-event is getting more and more urgent. Until then, I’m going to stick with nginx.

Updates

2011.10.27. Some of the config files have been updated for better performance and eliminated redundancy.


Comments
  1. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    I agree that for real production it's better to compile nginx depending on your needs, but for the basics, you do not really need dotdeb/compilation. Anyway, thank, for sharing these.
  2. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    my quick notes for debian: apt-get install build-essential apt-get install libpcre3-dev libssl-dev cd /opt/ wget http://nginx.org/download/ version.tar.gz --- GO TO THIS WEBSITE AND FIND WHATEVER VERSION YOU WANT tar -zxvf nginx-1.0.0.tar.gz - use the version you picked cd /opt/nginx-1.0.0/ -use version you picked ./configure --prefix=/opt/nginx --user=nginx --group=nginx --with-http_ssl_module make make install adduser --system --no-create-home --disabled-login --disabled-password --group nginx wget -O init-deb.sh http://library.linode.com/assets/682-init-deb.sh mv init-deb.sh /etc/init.d/nginx chmod +x /etc/init.d/nginx /usr/sbin/update-rc.d -f nginx defaults /etc/init.d/nginx start php-fpm setup dot deb package: nano /etc/apt/sources.list deb http://packages.dotdeb.org squeeze all deb-src http://packages.dotdeb.org squeeze all wget http://www.dotdeb.org/dotdeb.gpg cat dotdeb.gpg | apt-key add - apt-get update rm dotdeb.gpg apt-get install php5-cli php5-common apt-get install php5-fpm php5-cgi maybe these will help you, maybe they will help someone else, feel free to maybe improve if something here is better than what you do, doesn't matter to me
  3. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    you are editing `/etc/php5/fpm/pool.d/www.ini`, but in `/etc/php5/fpm/main.conf` you are including `/etc/php5/fpm/pool.d/*.conf`, which seems that you are accepting the original `/etc/php5/fpm/pool.d/www.conf` instead of your edited `.../www.ini` - is this correct? also, are you running php5-fpm as a `service`?
  4. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    Is there any specific reason you take that route to installing APC rather than apt-get install php-apc?
  5. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    Oh. I forgot that you need the nginx PPA. I edited the beginning of the post, that's going to do it.
  6. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    "Couldn't find package nginx-full"
  7. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    Thanks for posting these configs, these are the only working ones I've found for nginx/php5-fpm/mysql on Debian Squeeze. If mysql fail to start, try this: - rename the /etc/mysql/my.cnf to /etc/mysql/my.cnf.squeeze - rename the /etc/mysql/my.cnf.dpkg-dist to /etc/mysql/my.cnf - start the mysql daemon: /etc/init.d/mysql start. x500
  8. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    Since the PHP working directory is limited too, you need to place tmp inside otherwise it won't work. If you can suggest a safer config, please post it, but without tip, your comment is not much help.
  9. WordPress 3.0 Network with nginx and php-fpm backend on Ubuntu 11.04 (server)

    Pointing the tmp (upload) to a place inside the web-server space is not the best practice in web-server configurations. [wordpress doesn't help much on that either] Cheers, :)

© Peter Molnar Photo of Peter Molnar https://petermolnar.eu/pgp.asc github flickr