Ubuntu Hardy - Nginx, rails and mongrels

Following from the mongrel and mongrel cluster article, we can now look at configuring Nginx to proxy to a mongrel cluster so we can serve our Ruby on Rails application.

The process is very easy and can simply be repeated for serving multiple domains.


Setup

Firstly you will have needed to follow the previous Nginx articles and installed via aptitude or via source.

You will also need to have installed mongrels as per the article linked to above.

Plan

The plan is very simple:

We'll create a basic rails application and use 3 mongrels running from port 5000 to 5002.

Beyond creating the cluster itself, I won't go into detail as the mongrel and mongrel cluster article shows how to install and create mongrel clusters.

Once that is done, we'll create a simple vhost to proxy requests to the mongrel cluster.

Simple as that.

Rails Application

To create a rail application, move into your public_html folder:

cd /home/demo/public_html

and create a new Ruby on Rails application:

rails railsapp

Done.

Mongrel Cluster

Ensure you are in the rails folder:

cd ~/public_html/railsapp

Then create a mongrel cluster file as such:

mongrel_rails cluster::configure -e production -p 5000 -N 3 -c /home/demo/public_html/railsapp -a 127.0.0.1

It's always a good idea to check the created file (config/mongrel_cluster.yml):

cwd: /home/demo/public_html/railsapp
log_file: log/mongrel.log
port: "5000"
environment: production
address: 127.0.0.1
pid_file: tmp/pids/mongrel.pid
servers: 3

Now create a symlink to the /etc/mongrel_cluster folder (this ensures the cluster is restarted on a reboot):

sudo ln -s /home/demo/public_html/railsapp/config/mongrel_cluster.yml /etc/mongrel_cluster/railsapp.yml

Now all we need to do is start the cluster:

mongrel_cluster_ctl start

Done.

Nginx Virtual Host

Let's create the Nginx vhost:

sudo nano /etc/nginx/sites-available/domain.com

Note: If you installed Nginx from source, the path may vary to something like:

sudo nano /usr/local/nginx/sites-available/domain.com

The contents of the file are as such:

upstream domain1 {
        server 127.0.0.1:5000;
        server 127.0.0.1:5001;
        server 127.0.0.1:5002;
    }

server {
            listen   80;
            server_name  www.domain.com;
            rewrite ^/(.*) http://domain.com permanent;
           }


server {
            listen   80;
            server_name domain.com;

            access_log /home/demo/public_html/railsapp/log/access.log;
            error_log /home/demo/public_html/railsapp/log/error.log;

            root   /home/demo/public_html/railsapp/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;
                          }
            }

}

Take each section at a time and you will see that the basics are the same as for a 'normal' Nginx vhost.

We have the server_name, listen, log and index variables.

Where it differs is the addition of the Rails proxy settings.

In this example, we will use 3 mongrels running on ports 5000, 5001 and 5002 which I have defined in the 'upstream' setting. I've called it domain1 for ease of use.

The location settings say that if the requested file exists to serve the static version straightaway.

If the requested file doesn't exist, pass the request on to the mongrels. In this case it would proxy to 127.0.0.1:5000, 5001 and 5002 as those are the ports we defined at the beginning of the file.

Enable

Remember that we need to 'enable' any available vhosts or it won't be served (an easy thing to leave out).

Referring to the Nginx articles, all we need to do is create a simple symlink:

sudo ln -s /etc/nginx/sites-available/domain.com /etc/nginx/sites-enabled/domain.com

Or, if you installed from source:

sudo ln -s /usr/local/nginx/sites-available/domain.com /usr/local/nginx/sites-enabled/domain.com

Done.

Restart

Final step is to restart Nginx:

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

Use the 'stop' and 'start' method as issuing a restart command does not always work with Nginx.

Navigate

All that's left is to navigate to your domain:

http://www.domain.com

Where you will be greeted with the rails welcome page.

Summary

Setting up Nginx virtual hosts to proxy to a mongrel cluster is fairly simple.

To serve multiple domains, simply repeat the process with the new domain details.

PickledOnion

Article Comments:

John Topley commented Thu Aug 07 17:32:25 UTC 2008:

I'm guessing that this config doesn't use Nginx to serve static assets such as images i.e. everything goes through Rails?

Puzzled commented Sat Aug 09 20:52:16 UTC 2008:

Great article.

I've gotten proxying to work. The problem is that static content is served much slower through nginx than through mongrel directly.

For example:

http://example.com/static_file.jpg

is served at <10K/s. But:

http://example.com:3000/static_file.jpg

is served at 400+ K/s.

I can stop the mongrel process and still download the file at:

http://example.com/static_file.jpg

but the transfer rate is still < 10K/s.

(note to John, yes, I had to set up the config file to handle images separately, apparently).

Sujay commented Sun Aug 24 07:24:28 UTC 2008:

Hi,

I could run this for http://llet.in .. but I want to create a sub-domain .. bu .. so that my site becomes http://bu.llet.in .. how can I do that?

davos commented Sun Sep 07 03:00:56 UTC 2008:

Great article - you got me up & running in no time.

thanks!!

Nellboy commented Thu Jun 11 16:30:56 UTC 2009:

BIG FYI here for anyone using this and having problems with missing pids. I've had a lot of trouble with this.

the reason is due to the fact that mongrel_cluster requires RubyGems >= 1.3.1, yet 1.2.0 is the highest it goes when you issue gem update --system

the workaround is to issue 'sudo gem install rubygems-update', followed by 'sudo update_rubygems'

I had many hours of tearing my hair out with this, so hopefully this will save somebody else that trouble

cheers

Want to comment?


(not made public)

(optional)

(use plain text or Markdown syntax)