nginx, Yet Another Rails Deployment Option

There are a nauseating array of options to choose from when deciding how you’re going to stick your Ruby on Rails application up on a web server. There is no real canonical formula (yet), and depending on your needs, there may be multiple passable options—though none of them are entirely pretty or elegant.

The most common stack for a while was Apache or Lighty using FCGI. The release of Mongrel has completely changed that. Mongrel is in itself a very basic web server, similar to WEBrick. Mongrel is capable of serving up your Rails application over HTTP without FCGI. Unfortunately, it is very limited (can’t host multiple sites, doesn’t have a rewrite library, doesn’t support SSL, etc), so in most cases you’ll have to stick another server out in front of it to handle those things and then proxy Rails requests over to Mongrel. So, the question is, what do you put out in front of Mongrel to proxy requests?

I’m not going to discuss all the options, but if you’re looking for what appears to be (at the moment) a setup that is a breeze to install and will churn out more requests/second than anything else, read on.

One of the downsides to this setup is that the reverse proxy server we will be using, nginx, is a Russian project with almost no English documentation. Although, there seems to be at least two capable Rails developers (1, 2) who are vouching for its speed and stability.

  • The Pros:

    • This setup will probably squeeze out more requests/second out of your Rails app than any other.
    • It’s fairly simple to setup (if your setup is simple).
    • It’s very lightweight, using much less memory than most traditional FCGI setups.

  • The Cons:

    • Not many people are using nginx, so you’ll be living on the edge if you choose to do so.

    • I can only find one case of a Rails developer who is using nginx in a production setup, although he appears to be very happy with it.

    • The fact that it exists mostly in the nebulous world of the Russian internets means we don’t have a good sense of how secure/stable/tested it is. The lack of a reputation in the English speaking world is a little unsettling.

The tools:

  • nginx – “a high perfomance http and reverse proxy server”
  • Mongrel – “a fast HTTP library and server for Ruby that is intended for hosting Ruby web applications of any kind using plain HTTP rather than FastCGI or SCGI.”
  • Mongrel_cluster – “a GemPlugin that wrappers the mongrel HTTP server and simplifies the deployment of webapps using a cluster of mongrel servers. Mongrel_cluster will conveniently configure and control several mongrel servers, or groups of mongrel servers, which are then load balanced using a reverse proxy solution.”
  • Ubuntu Server or Debian
  • Your Ruby on Rails applications

The final setup should look like this:

  1. For every Rails application, you will have 1 Mongrel cluster, each with multiple Mongrel instances (processes).
  2. nginx sits in front, accepting connections from clients.
  3. If static content is requested, nginx serves it up directly. nginx is capable of serving up static files very quickly.
  4. If dynamic content is requested, the request is proxied off to the appropriate Mongrel cluster.
  5. One of the processes of the Mongrel cluster receives the request, processes the request through your Rails application, and returns the response to the client.

So, we’ll start with a basic installation of Ubuntu or Debian and end with an awesome Rails stack serving up multiple websites. This isn’t going to be a step by step tutorial, but rather what is hopefully enough hastily patched together tidbits to help you hit the ground running.

  1. First, get Ubuntu up and running with a bare bones install. In my case, my VPS provider did this for me.
  2. Setup the basic Rails tools you will need on the server such as Subversion, Ruby, ImageMagick, RMagick, MySQL, SSH. A great guide for this can be found over at brainspl.at. If you follow that tutorial, DO NOT INSTALL LIGHTTPD AND FCGI. It is totally unnecessary for our setup.
  3. Get your Rails projects onto the server, either via Subversion checkout, Capistrano, or whatever it is you prefer.
  4. Setup Mongrel and Mongrel_cluster. The former tutorial will detail how to setup your Mongrel cluster to start at boot. Unfortunately, this only works on Debian and NOT Ubuntu. You can find a fix here.
  5. Make sure your Mongrel cluster works before proceeding. You can connect directly to one of the Mongrel instances that you started up if you don’t have a firewall blocking its port. One of my Rails apps had 2 Mongrel processes listening on ports 5001 and 5002. I connected to http://domain1.com:5001 and the page loaded just fine. If you don’t have this much working, there’s no point in proceeding.
  6. Make sure Apache and Lighttpd are not installed on the system, and nothing is using port 80. (netstat -an | grep 80)
  7. Read this and compile/install nginx. After you follow those directions, there are only three more quick steps:

    1. Setup the config file at /usr/local/nginx/conf/nginx.conf. In the brainspl.at tutorial he gives you his sample config file. This demonstrates how to serve up static files with nginx and proxy dynamic requests to Mongrel. This should be enough for most people, unless you are using SSL or need virtual hosting. In which case, you might find these “Typical Configurations” very helpful. Here is my configuration file which uses virtual hosting to serve up requests to the appropriate Mongrel cluster, and only passes dynamic requests to Mongrel while having nginx itself serve up static requests.
    2. Create an nginx init.d script. An init.d script is an easy way to start/stop/restart a daemon. It goes in /etc/init.d and commands will typically look like:

      /etc/init.d/nginx start /etc/init.d/nginx stop

      To set this up, perform these commands:

      wget http://notrocketsurgery.com/files/nginx \ -O /etc/init.d/nginx chmod 750 /etc/init.d/nginx

      This makes it easy to manage nginx. Give it a shot and make sure it’s working! (Disclaimer: I made this init.d script from the /etc/init.d/skeleton file without too much knowledge of what everything meant, so feel free to post corrections/improvements in a comment below)
    3. Setup nginx to start at boot.

      update-rc.d nginx defaults

      This takes the /etc/init.d/nginx script above and makes all the appropriate references to it in the runlevel scripts. Yeah, that wasn’t English to me either the first time I read it. First, read up on what runlevels are. So, what you need is the operating system to call /etc/init.d/nginx start and /etc/init.d/nginx stop at the appropriate runlevels. That is what the above command does automatically for you! You can read up more in the man page (man update-rc.d). Try restarting your server and make sure that both the Mongrel processes and the nginx daemon are still running after you restart. A quick look at pstree will verify this.

Thanks to Ezra Zygmuntowicz and Alexey Kovyrin for posting so much helpful information on their blogs which pretty much make up almost all the content in this post. Thanks to Zed Shaw for making Mongrel and being a cool guy.