bye Redskin, zdravstvujte Redstar (NginX tips)

on May 18, 2007

female readers: Warning! geek content!

Lately I started realizing that there is a new breed of system administrators. I used to think they just had a boring job maintaining systems and playing BSD snake. But I've come to realize that there are also Agile Sysadmins, the Agmin. These Agmins are constantly adapting systems for performance and scalability. From trying out weird undocumented software to doing mashups with Amazon S3.

At the moment I'm doing a private project that requires a lot of focus on system operations. I am not the best guy for that I have to admit. I can be organized and disciplined, but not in the required maintaining fashion. A skill that is also required by the Agmin. I'm not an agmin (more about this later, because we really need an agmin for this project).

Anyway, I decided to plug out the big ol' Apache server and decided to switch to the ultra-light and fast HTTP server, NginX. The primary argument is obviously performance, second is the philosophy of less is more.

NginX is a light http server coded by some russians. They often use a communist red star in their logo. English documentation is limited, but as a Ruby-ist, I'm used to that. Most of the useful good documentation in the Ruby community comes from blog posts (human conversation and problem solving). OK let's start.

There are a few things that this article will cover:

  • Setting up PHP
  • Wordpress fancy URL rewrites
  • An alternative for mod_userdir
  • Example configuration of php5-fastcgi + rails-userdir hack

First of all, when configuring NginX, you better know regular expressions! (cheat sheet here NginX has a pretty basic configuration syntax (no XML, humans configure it afterall). Things I found particularly useful: rewrite and include).

Setting up PHP

Assuming you have debian, you need the 'php5-cgi' binary (or php4-cgi in case you're slow). We will put this binary in 'fastcgi mode', allowing NginX to execute code on it through sockets:

  #!/bin/bash 
  PHPFCGI="/usr/bin/php5-cgi"
  FCGIPORT="8085"
  FCGIADDR="127.0.0.1"
  PHP_FCGI_CHILDREN=4
  PHP_FCGI_MAX_REQUESTS=1000
  USERID=www-data
  EX=" $PHPFCGI -b $FCGIADDR:$FCGIPORT"
  echo $EX
  nohup env - PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS  USERID=$USERID sh -c "$EX" &> /dev/null &

This script is a modified version of this russian's one. You can call this script phpd ;)

To enable PHP to your vhost/docroot use this:

fastcgi_pass   127.0.0.1:8085;
fastcgi_index  index.php;

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_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;

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;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

I called it enable_php5, so I can include it in my NginX location directives:

      include        /etc/nginx/enable_php5;

Wordpress Fancy URL rewrites

To enable urls like: http://digigen.nl/2007/05/07/confabiocom-technology-revealed/ you need some special rules. Basically if the file doesn't exist, pass the entire request to wordpress.

/etc/nginx/enable_wordpress:

    # wordpress pretty urls
    # http://drupal.org/node/110224
    location / {
        index index.php;
        if (-f $request_filename) {
            break;  
        }       
        if (-d $request_filename) {
            break;  
        }       
        rewrite ^(.+)$ /index.php?q=$1 last;
    }

An alternative for mod_userdir

In apache you have directory lists by default. I could not find this in NginX so I hacked up a RubyOnRails controller that will handle this:

map.connect 'userdir/:user/:path', :controller => "userdir", :action => 'index', :path => '', :requirements => {:path => /.*/}

Note: users can customize this view by creating ~/www/.home.rhtml or ~/www/.home.css

In your NginX configuration (enable_userdir):

   # enable homedirs
    location ~ /~(.*) {
      rewrite  ^/~(.*)$  /userdir/$1 break;
      include        /etc/nginx/enable_mongrel;
      proxy_pass     http://127.0.0.1:8001;
    }

This is to be added to the Rails routes.rb file. All code for this userdir hack is here. Let me know if/when there is a sane solution for this.

note: AutoIndexModule looks like a sane solution (thanks Drakonen)

Example configuration of php5-fastcgi + rails-userdir hack

server {
    server_name     digigen.nl www.digigen.nl;
    access_log      /var/log/nginx/digigen.nl.access.log;
    index           index.php;
    root            /www/vhosts/digigen.nl;

    include /etc/nginx/enable_userdir;
    include /etc/nginx/enable_wordpress;

    location ~ \.php$ {
      fastcgi_param  SCRIPT_NAME  /www/vhosts/digigen.nl$fastcgi_script_name;
      fastcgi_param  SCRIPT_FILENAME  /www/vhosts/digigen.nl$fastcgi_script_name;
      include        /etc/nginx/enable_php5;
    }
}

I hope this might help some people struggling with the strong taste of Vodka:

 /etc/init.d/apache stop
 /etc/init.d/nginx start # it's ok, the cold war is over

2 Responses to “bye Redskin, zdravstvujte Redstar (NginX tips)”

  1. Chu Yeow says:
    Nice write up, thanks! Just curious, how are you starting your fcgi processes on boot (i.e. how do you ensure your phpd script is run on system init)? I found an init script for managing php-cgi processes, but I'm curious to see how you've set it up there for yourself.
  2. Dominiek says:
    Hey Chu, Hey, I also found a script to do that but I modified that and fit it to my own needs:
    #!/bin/bash
    PHPFCGI="/usr/bin/php5-cgi"
    FCGIPORT="8085"
    FCGIADDR="127.0.0.1"
    PHP_FCGI_CHILDREN=4
    PHP_FCGI_MAX_REQUESTS=3
    USERID=www-data
    EX=" $PHPFCGI -b $FCGIADDR:$FCGIPORT"
    echo $EX
    nohup env - PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS USERID=$USERID sh -c "$EX" &> /dev/null &
    
    How's Singapore these days? Let me pop u an email sometime soon.

Sorry, comments are closed for this article.