Installing a New Engine – Nginx

I originally started blogging with WordPress via the one-click installer that came standard with a basic Network Solutions website.  It’s been several years since then, and I definitely advise against getting started that way.  There are better hosts out there, and better server systems.  Take your pick.

For the past year, I’ve been running my blogs on a VPS that I manage myself.  I got started with the basic LAMP (Linux-Apache-MySql-PHP) stack that just about everyone else has.  And it worked … for the most part.

Unfortunately, Apache is a bit slow on the VPS I have.  And to handle some more experimental projects I’m working on, I felt the need to upgrade from the standard to something a bit beefier.

Some developers recommended Nginx.  It’s an event-driven web server that can handle several concurrent connections, has a might lighter footprint than Apache, and can handle the new HTML5 websockets that I plan to play with.

I was sold!

Except for one tiny detail.  All of the “how to install WordPress” tutorials out there detail installation with Apache … not with Nginx.  So I was mostly on my own, but if you’re reading this, it means I managed to pull it off!

Installing Nginx

The first thing to remember is that I run my server on CentOS 5.6.  If you’re using a different version of Linux, these steps might not work for you.  In addition, I might have forgotten 1 or 3 steps along the way … but this is essentially what I did to get things running.

First, I wanted to install Nginx.  It turned out to be a lot easier than you might think:

[cc:lang=sh]~# yum install nginx[/cc]

This seemed to install the server just fine, but I couldn’t be completely sure.  Since Apache was still running to serve up my sites (and my clients’ sites), I couldn’t bind Nginx to port 80.  Instead, I opened port 8080 temporarily and changed Nginix’s default site to listen on that port instead.

[cc lang=sh tab_size=”2″ width=”580″ height=”500″]
server {
limit_conn myzone 10;
listen 8080;
server_name _;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}

error_page 404 /404.html;

location = /404.html {
root /usr/share/nginx/html;
}

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ .php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
# location ~ .php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
# include fastcgi_params;
# }

# deny access to .htaccess files, if Apache’s document root
# concurs with nginx’s one
#
#location ~ /.ht {
# deny all;
#}
}
[/cc]

Loading my default site via port 8080 then returned exactly what I expected – the default Nginx “it works” page:

Installing PHP

Next, I needed to install PHP.  The Apache setup I was migrating from had PHP bundled together inside Apache.  Nginx decouples everything and runs PHP as a FastCGI process.  So I needed to install quite a bit of other tools to get things running.  Luckily, it just took one convoluted installation command:

[cc lang=sh width=”580″]
~# yum install php-pear-Net-Socket php-pear php-common php-gd php-devel php php-mbstring php-pear-Mail php-cli php-imap php-snmp php-pdo php-xml php-pear-Auth-SASL php-ldap php-pear-Net-SMTP php-mysql
[/cc]

This installs PHP and all of the various modules that it needs.  The one other element we need for Nginx to work with PHP is a process to spawn FastCGI processes:

[cc lang=sh width=”580″]~# yum install spawn-fcgi[/cc]

One more installation lets us configure an initialization script to spawn our FastCGI PHP handlers.  As much as I don’t like downloading scripts from strange sources, it looks clean enough and runs perfectly on my box:

[cc lang=sh width=”580″]
~# wget http://bash.cyberciti.biz/dl/419.sh.zip
~# unzip 419.sh.zip
~# mv 419.sh /etc/init.d/php_cgi
~# chmod +x /etc/init.d/php_cgi
[/cc]

You can test that PHP is installed by placing a standard [cci]phpinfo.php[/cci] file in the http root for Nginx.  This will serve two purposes: it will demonstrate that PHP is parsing properly and will help you verify that you installed the version of PHP you thought you were installing.

Just make sure you uncomment the “location” definition labelled “pass the PHP scripts to FastCGI server” so that the system knows what to do with the PHP.

Configuring WordPress

This was the tricky part.

I’ve been running multiple websites with distinct top-level-domains on the same WordPress Multisite setup.  On Apache, it was somewhat difficult to get my vhosts just right so that they’d work with the MU Domain Mapping plugin.  Really, it took me a few weeks to get that right.

So this morning, I kept toggling back and forth between Apache and Nginx.  When I thought I had a solution, I’d dump it in my Nginx conf file, test things, curse silently at the terminal, and restart Apache so no one would see my sites down.

Then, finally, I figured it out!

Below is an example configuration file for this particular domain:

[cc lang=sh width=”580″ height=”500″ tab_size=”2″]
server {
listen 80;
server_name mindsharestrategy.com *.mindsharestrategy.com;
root /home/s1/html/eamann;

location / {
root /home/s1/html/eamann;
try_files $uri $uri/ /index.php?$args;
index index.php;

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

location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico)$ {
rewrite ^.*/files/(.*(js|css|png|jpg|jpeg|gif|ico))$ /wp-includes/ms-files.php?file=$1 last;
expires 24h;
log_not_found off;
}

location ~ .php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/s1/html/eamann$fastcgi_script_name;
include fastcgi_params;
}

location @wp {
rewrite ^/files(.*) /wp-includes/ms-files.php?file=$1 last;

rewrite ^(/[^/]+)?(/wp-.*) $2 last;
rewrite ^(/[^/]+)?(/.*.php) $2 last;

rewrite ^/(.*)$ /index.php?q=$1 last;
}
}
[/cc]

All of my virtual hosts (discrete domains) are configured the same way. It might not be the prettiest configuration, but just about everything seems to work. Image uploads are still having issues, but my domains work, authentication works, content works, and WordPress works like a dream.

Performance Differences Over Apache

I’m running the server I have now because my old VPS kept crashing. Apache spawned too many processes and would run the entire system out of memory almost immediately. The best setup I could manage under Apache was to allow no more than 5 instances at a time. Even then, I frequently ran out of memory and all of my sites were somewhat slow.

Nginix, on the other hand, is running a minimum of 10 instances at any given time (I allow it up to 20), uses half the memory that Apache used, and serves up my WordPress sites in a fraction of the time.

Anecdotal, I know. But I’ll take a 2-second Nginx site over a 12-second Apache site any day of the week.

My next challenge is to get Node.JS running through a proxy so I can start playing with websockets …