How to: Nginx, FCGI, PHP, MySQL, Ruby On Rails, Rewrite

In a previous post I talked about how I just set up my new Slicehost account using Nginx to power this blog (Wordpress – PHP) and some Rails Apps. Here’s a mini how to on how I got the whole thing working, as its not quite as easy as it sounds.

First though – Why not use Apache + mod_proxy… There is a ton of documentation out there for Apache, and the majority of the web runs on it?

Well, Nginx is super tiny and super light weight and super fast. If you are in a situation like many people who are using Slicehost and a 256 MB slice, Apache has too big a resource foot print. If you are a Rails guy (or gal) Apache is just used as a proxy to send requests to Mongrel and Apache will just use resources that could be used else where. Nginx is stable, well supported, and growing in popularity. Plus the name is cool.. NGINX.. yeah…

This will (should) work with a fresh install of Ubuntu Server 7.10, but as always YMMV.

A lot of this is adapted from a great post on How To Forge, thanks 3uropa.

Install only the OpenSSH packages NOT the LAMP packages when installing Ubuntu Server 7.10. You’ll have to setup networking for whatever your situation is so can’t help you there. If you have a Slicehost slice, don’t do anything. :)
install ubuntu
install ubuntu 2

Once that is finished installing Ubuntu Server, the first thing you want to do is update and get some prerequisites out of the way!

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential

Ok. Now, you need to go in and get all your files/directories in order. That mean, go to /var/www/ (or wherever) and make directories for your PHP site. In my case, I used /home/joyerhot/public_html/johnyerhot.com/wordpress as the root of my Wordpress blog. You should be able to either use wget to grab stuff or SFTP in with your username and password.

Ok. Next we will install PHP5 and MySQL

apt-get install php5-cli php5-cgi build-essential mysql-server mysql-client libmysqlclitene15-dev

Now, lets get Nginx while we’re at it! The version that is in Ubuntu’s repos is old and not cool, so we’ll manually grab a newer version:

wget http://technokracy.net/nginx/nginx_0.5.32~grrr-1_i386.deb
sudo dpkg nginx_*

And now Nginx is running and listening to port 8000.. we’ll change that later though.
nginx setup

Now, here is where it gets sticky. You are going to create and edit /etc/nginx/sites-enabled/mysite

sudo nano /etc/nginx/sites-enabled/mysite

and put in it the following, changing directories and whatnot to what is appropriate for you.

server {

listen 80;
server_name yourdomain.com;
rewrite ^/(.*) http://www.yourdomain.com permanent;

}

server {

listen 80;
server_name www.yourdomain.com;

access_log /home/joyerhot/public_html/yourdomain.com/logs/access.log;
error_log /home/joyerhot/public_html/yourdomain.com/logs/error.log;

if (!-e $request_filename) {
rewrite ^([_0-9a-zA-Z-]+)?(/wp-.*) $2 last;
rewrite ^([_0-9a-zA-Z-]+)?(/.*\.php)$ $2 last;
rewrite ^ /index.php last;
}

location / {

root /home/joyerhot/public_html/johnyerhot.com/wordpress;
index index.html index.php index.htm;

}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
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_FILENAME /home/joyerhot/public_html/yourdomain.com/wordpress/$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;
}
}

editing_nginx

WHEW! There is A LOT of stuff there! This will setup a Virtual Host for your PHP site, and enable rewrites (for pretty urls). Here is what you need to change in yours though:

1. the server_name is whatever your domain is.
2. rewrite ^/(.*) http://www.whatever_goes_here.com permanent;
3. access_log directory
4. error_log directory
5. root directory
6. fastcgi_param SCRIPT_FILENAME /path/to/your/php/app/root/$fastcgi_script_name;
7. MAKE SURE YOU’RE LISTENING TO PORT 80.

Ok, now we’ll get PHP5 working as a fcgi process. To do this, we need to grab some of Lighttpd.

wget http://www.lighttpd.net/download/lighttpd-1.4.18.tar.bz2
tar -xvjf lighttpd-*
cd lighttpd*
./configure
make

DO NOT “make install”. :)

sudo cp src/spawn-fcgi /usr/bin/spawn-fcgi
sudo nano /usr/bin/php-fastcgi

Now, add the following into php-fastcgi:

/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 2 -u www-data -f /usr/bin/php5-cgi

-C controls how many fcgi instances of PHP5 are spawned, so you can put however many are appropriate for you in there. I only needed 2.

sudo nano /etc/init.d/init-fastcgi

And add:

#!/bin/bash
PHP_SCRIPT=/usr/bin/php-fastcgi
RETVAL=0
case “$1″ in
start)
$PHP_SCRIPT
RETVAL=$?
;;
stop)
killall -9 php
RETVAL=$?
;;
restart)
killall -9 php
$PHP_SCRIPT
RETVAL=$?
;;
*)
echo “Usage: php-fastcgi {start|stop|restart}”
exit 1
;;
esac
exit $RETVAL

Do some permission magic:

sudo chmod 755 /etc/init.d/init-fastcgi
sudo chmod 755 /usr/bin/php-fastcgi

Now, you can make sure everything is working by:

/etc/init.d/init-fastcgi start
top #then shift + M

You should see a couple of PHP5 fcgi processes! YAY!
Finally, we want them to start on reboots and whatnot so…

update-rc.d init-fastcgi defaults

Ok, now lets restart Nginx and see if things are working!

/etc/init.d/nginx restart

You should be able to hit your Php app now!

Now for Ruby and friends!

sudo apt-get install libmysql-ruby1.8 ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby irb1.8 libdbd-mysql-perl libdbi-perl libmysql-ruby1.8 libmysqlclient15-dev libmysqlclient15off libnet-daemon-perl libopenssl-ruby libopenssl-ruby1.8 libplrpc-perl libreadline-ruby1.8 libruby1.8 mysql-client mysql-client-5.0 mysql-common mysql-server mysql-server-5.0 rdoc1.8 ri1.8 ruby1.8 ruby1.8-dev zlib1g-dev

And you might need to create some symlinks (I had to), thanks Vince Wadhwanl.

sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb

Make sure things are working ok…

ruby -v

You should get that you have 1.8.6 installed.Ok, now lets install RubyGems.

wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
tar xvzf rubygems-1.0.1.tgz
cd rubygems-1.0.1
sudo ruby setup.rb
sudo ln -s /usr/bin/gem1.8 /usr/bin/gem

And finally for Rails and Mongrel:

sudo gem install rails
sudo gem install mongrel mongrel_cluster

You can now set up a mongrel_cluster or whatever you need. There’s plenty of tutorials out there for that, so Google is your friend. The rest will assume you have your Rails app up and running on port 3000.
We now have to set up a site profile for Nginx for your mongrel_cluster.

sudo nano /etc/nginx/sites-enabled/rails_app

And add to it, once again changing the relevant parts….

upstream domain1 {
server 127.0.0.1:3000;
}

server {

listen 80;
server_name www.your2nddomain.com;
rewrite ^/(.*) http://subdomain.your2nddomain.com permanent;

}

server {

listen 80;
server_name subdomain.your2nddomain.com;

access_log /home/joyerhot/public_html/your2nddomain.com/logs/access.log;
error_log /home/joyerhot/public_html/your2nddomain.com/logs/error.log;

location / {

root /home/joyerhot/public_html/your2nddomain.com/public/RAILS_ROOT/public/;
index index.html;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;

if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}

if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}

if (!-f $request_filename) {
proxy_pass http://domain1;
break;
}
}

}

}

Again, heres what you need to change:
1. server_name should be the domain
2. In this case the “rewrite” line will point to subdomain.your2nddomain.com, you should prolly just put your domain here(www.your2nddomain.com).
3. access_log directory
4. error_log directory
5. root directory (pointing to RAILS_ROOT/public)

That’s it! You should now restart Nginx (/etc/init.d/nginx restart) and when you should have everything working! You’ve got Virtual Hosts setup with PHP running as Fast CGI on one domain and Ruby On Rails on the other!

I’m sure I’ve got a typo or two in here and a mistake here and there, please leave a message if this worked or didn’t work for you. Remember, this is the process I went through getting everything working on my Slicehost Slice and another server at work, both running Ubuntu 7.10.

PS. Here is one little nugget of info that may be helpful – Your MySQL socket file location (for your RAILS_ROOT/config/database.yml file) is located at /var/run/mysqld/mysqld.sock. I didn’t realize this at first. :)

Tags: , , , , , , , ,

15 Responses to “How to: Nginx, FCGI, PHP, MySQL, Ruby On Rails, Rewrite”

  1. fak3r says:

    This is a *great* post, as someone who has a ton of Wordpress sites currently hosted on Debian (Etch) running Lighttpd, converting everything to Nginx has been a challenge – but I think this about covers it! (I’ll let you know if it doesn’t) I write HOWTOs on my site, so I can appreciate the time and effort you put into yours. Only suggestion I’d have is to include Xcache to your PHP5 install to speed up PHP generation. On Ubuntu/Debian would only require something like this:


    apt-get install php5-xcache
    /etc/init.d/nginx restart

    Then check your php-info file to make sure xcache loaded and you’re done.

    Thanks again

    fak3r

  2. John says:

    @fak3r
    thanks man, thats awesome!

  3. Nuno Zmas says:

    Neat enhancement over the original HowtiForge tutorial.
    Nginx+PHP/FastCGI is in deed a lot lighter than Apache and the rewrite rules for WP work under Joomla as well. A recent discovery I stumbled upon after some experiments :)
    There’s just one thing left to solve. I can’t seem to run perl scripts.
    The recipe published at http://wiki.codemongers.com/NginxSimpleCGI did not work for me so far :(

  4. nginx says:

    [...] [...]

  5. Dilip P says:

    Nice Article. I just wanted to point out a typo.

    // apt-get install php5-cli php5-cgi build-essential mysql-server mysql-client libmysqlclitene15-dev

    it should be libmysqlclient15-dev

    (i.e) // apt-get install php5-cli php5-cgi build-essential mysql-server mysql-client libmysqlclient15-dev

  6. [...] How To: How to: Nginx, FCGI, PHP, MySQL, Ruby On Rails, Rewrite, Vhosts | John Yerhot (tags: nginx rails fcgi php mongrel setup ubuntu) [...]

  7. [...] more difficult part is getting php installed with a fastcgi initiator. This was the best guide I ultimately managed to find. It fit my purposes because it compiled lighttpd, but didn’t make install it (which would have [...]

  8. roger says:

    the link to the “updated version” is not working (?)

  9. John says:

    Sorry, I ended up taking down my other blog with the ‘updated version’ so I’ve removed that link. This one should still be good. :)

  10. Jimmy says:

    Thanks for this great article, your examples worked out of the book.

    There is however a small problem regarding php via fcgi.

    fcgi allows for a certain count of requests per child (in this example we have no config-file for fcgi afaik)
    while a child is closing, a request might be made and channeled to the fcgi process (which is still closing) and thus this request fails.

    Here is the output of my benchmark:

    [quote]
    Document Path: /phpinfo.php
    Document Length: 193 bytes

    Concurrency Level: 1000
    Time taken for tests: 15.180180 seconds
    Complete requests: 100000
    Failed requests: 3643
    (Connect: 0, Length: 3643, Exceptions: 0)
    Write errors: 0
    Non-2xx responses: 96358
    Total transferred: 276542058 bytes
    HTML transferred: 260356636 bytes
    Requests per second: 6587.54 [#/sec] (mean)
    Time per request: 151.802 [ms] (mean)
    Time per request: 0.152 [ms] (mean, across all concurrent requests)
    Transfer rate: 17790.30 [Kbytes/sec] received
    [/quote]

    Any ideas on how to remedy this minor yet annoying issue ?

    Thanks,
    /Jimmy

  11. Nnyan says:

    I download the latest version of lighttpd (1.4.24) and I can not find the spawn-fcgi anywhere. Any help would be greatly appreciated.

  12. Nnyan says:

    OK found that if I enabled the EPEL repo’s I could do a simple yum install spawn-fcgi

  13. Nnyan says:

    ok so got past that part and get this error:

    [@gandalf bin]# /etc/init.d/init-fastcgi start
    /etc/init.d/init-fastcgi: line 19: stop: command not found
    /etc/init.d/init-fastcgi: line 19: restart}.: command not found

Leave a Reply