Since purchasing a slice of heaven a few days ago, I’ve setup a very lightweight Rails stack consisting of Thin & nginx for my Rails needs. Since I went for a slice with just 256MB of RAM, memory consumption becomes a pretty serious issue. nginx has been around for quite a while now, and has recently started to become more and more popular in Rails deployments due to the fact that’s incredibly lightweight, very fast and stable - perfect not only for VPS jerks like me, but for anyone who really doesn’t feel it’s necessary to run Apache for static content/cluster proxying/load balancing. Thin is something I came across very recently and I decided to try is as a replacement for mongrel since I’d heard some great things about it, even if it is still alpha. It’s performance in comparison to mongrel (even with a tacked on event machine) looks very impressive on paper.

Setting up clustering with it is a snap, just spawn the processes and pipe them into nginx. Here’s a startup script borrowed from Stephen Celis:

\
namespace :thin do\
 namespace :cluster do desc Start thin cluster\
 task :start =\> :environment do\
 \`cd \#{RAILS\_ROOT}\`\
 port\_range = RAILS\_ENV == development ? 3 : 8\
 (ENV[SIZE] ? ENV[SIZE].to\_i : 4).times do |i|\
 Thread.new do\
 port = ENV[PORT] ? ENV[PORT].to\_i + i : (\#{port\_range}%03d”  i)
          str  = "thin start -d -p\#{port} -Ptmp/pids/thin-\#{port}.pid"
          str += " -e\#{RAILS\_ENV}"
          puts str
          puts "Starting server on port \#{port}..."
          \`\#{str}\`
        end
      end
    end
    desc 'Stop all thin clusters'
    task :stop => :environment do
      \`cd \#{RAILS\_ROOT}\`
      Dir.new("\#{RAILS\_ROOT}/tmp/pids").each do |file|
        Thread.new do
          if file.starts\_with?("thin-")
            str  = "thin stop -Ptmp/pids/\#{file}"
            puts "Stopping server on port \#{file[/\\d+/]}..."
            \`\#{str}\`
          end
        end
      end
    end
  end
end

Then spawn however many processes you want using something like:

rake thin:cluster:start RAILS\_ENV=production SIZE=2 PORT=3000

To stop them, use:

rake thin:cluster:stop

With that all nicely setup, you can use an nginx config similar to mine to get things in order:

upstream dapperjerk {
  server 127.0.0.1:3000;
  server 127.0.0.1:3001;
  }

server {
  listen   80;
  server\_name  www.dapperjerk.com;
  rewrite \^/(.\*) http://dapperjerk.com permanent;
}

server {
  listen   80;
  server\_name dapperjerk.com;

  access\_log /home/jason/public\_html/blog/log/access.log;
  error\_log /home/jason/public\_html/blog/log/error.log;

  root   /home/jason/public\_html/blog/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://dapperjerk;
      break;
    }
  }

}

And that’s really all there is too it. I’m not a masochist so I’ve never bothered to fully read Apache’s documentation, but I don’t think I’m going out on a limb here by saying that it seems to be a lot easier to manage nginx. I haven’t really put thin through its paces yet, but we’ll see in the coming weeks as I cobble together a custom blog app to run this place.