Running Sinatra (and other Rack apps) on Nginx + Unicorn

Like it often happens with Open Source projects, documentation for Unicorn is quite a mess. I was searching for a tutorial on how to deploy a simple ‘Hello, world!’ Sinatra app on Nginx + Unicorn, but all tutorials I found were somewhat lacking. With some help from vjt I managed to get it up and running, so here’s the breakdown. Most config files are adapted from original project examples.

As you can imagine, the first step is to install all the packages. I’m using an Ubuntu 10.04 virtual machine, so:

user[~]% sudo aptitude install ruby-full rubygems nginx
user[~]% sudo gem install unicorn sinatra

Then, make sure Ruby gems are in PATH:

user[~]% echo 'PATH=$PATH:/var/lib/gems/1.8/bin' >> /etc/environment
user[~]% source /etc/environment
user[~]% echo $PATH
user[~]% unicorn --version
unicorn v4.1.1

Let’s create a basic Sinatra app:

user[~]% mkdir basic; cd basic/
user[~/basic/]% cat > app.rb <<EOF
require 'rubygems'
require 'sinatra'

get '/' do
  "hello world! it's #{} here!"


Let’s verify the app is working with WEBrick (Sinatra development webserver):

user[~/basic/]% ruby app.rb
[2012-01-01 21:03:58] INFO  WEBrick 1.3.1
[2012-01-01 21:03:58] INFO  ruby 1.8.7 (2010-01-10) [x86_64-linux]
== Sinatra/1.3.1 has taken the stage on 4567 for development with backup from WEBrick
[2012-01-01 21:03:58] INFO  WEBrick::HTTPServer#start: pid=1474 port=4567

Try connecting to the host on port 4567:

user[~]% curl http://localhost:4567
hello world! it's Sun Jan 01 21:08:14 +0100 2012 here!

Everything is working fine. Stop WEBrick (Control + C) and let’s create unicorn config files:

user[~/basic/]% cat > <<EOF
require 'sinatra'

set :env,  :production
disable :run

require './app.rb'

run Sinatra::Application

user[~/basic/]% cat > unicorn.conf <<EOF
worker_processes 8
working_directory "/home/USERNAME/basic"
listen 'unix:/tmp/basic.sock', :backlog => 512
timeout 120
pid "/var/run/unicorn/"

preload_app true
if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true

before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && != old_pid
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us


Make sure /var/run/unicorn is writable by the user that will run unicorn. Try starting the daemon:

user[~]% cd basic
user[~/basic/]% unicorn -c unicorn.conf
I, [2012-01-10T14:13:40.886917 #6288]  INFO -- : unlinking existing socket=/tmp/basic.sock
I, [2012-01-10T14:13:40.887076 #6288]  INFO -- : listening on addr=/tmp/basic.sock fd=3
I, [2012-01-10T14:13:40.887287 #6288]  INFO -- : Refreshing Gem list
I, [2012-01-10T14:13:40.951007 #6291]  INFO -- : worker=2 spawned pid=6291
I, [2012-01-10T14:13:40.951180 #6291]  INFO -- : worker=2 ready
I, [2012-01-10T14:13:40.951426 #6289]  INFO -- : worker=0 spawned pid=6289
I, [2012-01-10T14:13:40.951563 #6289]  INFO -- : worker=0 ready
I, [2012-01-10T14:13:40.951960 #6288]  INFO -- : master process ready

You can check if the (UNIX) socket is up:

user[~]% sudo netstat -nalx
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     17669    /tmp/basic.sock

Now let’s change the nginx configuration. I modified the example from Unicorn website:

user[~]% sudo cat > /etc/nginx/nginx.conf <<EOF
worker_processes 1;


pid /var/run/;
error_log /var/log/nginx/error.log;

events {
  worker_connections 1024;
  accept_mutex off;
  use epoll;

http {
  include mime.types;
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;
  sendfile on;

  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/html text/xml text/css
             text/javascript application/x-javascript

  upstream app_server {
    server unix:/tmp/basic.sock fail_timeout=0;

  server {
    listen 80 default deferred; # for Linux

    client_max_body_size 4G;
    server_name _;

    keepalive_timeout 5;

    # path for static files
    root /home/USERNAME/basic/public;

    # Prefer to serve static files directly from nginx to avoid unnecessary
    # data copies from the application server.
    try_files $uri/index.html $uri.html $uri @app;

    location @app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;


Restart nginx:

user[~]% sudo service nginx restart
Restarting nginx: [warn]: duplicate MIME type "text/html" in /etc/nginx/nginx.conf:68
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful
[warn]: duplicate MIME type "text/html" in /etc/nginx/nginx.conf:68

Check that nginx is running:

user[~]% sudo netstat -nalt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0    *               LISTEN     

Try loading the website:

user[~]% curl localhost
hello world! it's Tue Jan 10 14:21:04 +0100 2012 here!

Here you go! You should work some magic (or just download one of many startup scripts) for running Unicorn at boot time, then sit down and relax :)

Edit: fixed paths and a call to sudo.