Installing Rails 3 nginx unicorn on a shared web host non-root

Lately I tried to install a complete Rails 3 nginx unicorn web stack on a shared host without root privileges, in my user home directory.

  1. Ruby and Rails3 setup
  2. nginx setup
  3. Unicorn setup
  4. Unicorn setup with nginx

In this process, here are the software versions used:

  • Ruby: 1.8.7 p370
  • RubyGems: 1.6.2
  • Rails: 3.2.12
  • nginx: 1.2.7
  • Unicorn: 4.5.0

For info, I tested this setup successfully on a PlanetHoster’s shared web host.

In this tutorial, the user account is named “myuser”, belonging to the group “mygroup”. The web server (nginx) will run as this user (non-root), therefore in on a port number >1024 (I chose 12006). The Rails3 application is named “myapp”, and is accessible using the external url “http://my_public_url.com”

Before beginning, it is important to make sure development tools (at least gcc, ld and make) are available on the shared web host environment, and that you have an SSH access to your web host.

Here are the steps to get it running:

Ruby and Rails3 setup

  1. Setup your RubyGems config to install gems locally in your home directory (if not already done).
    This is done first by setting a few environment variables:

    export GEM_PATH="/home/myuser/gems:/usr/lib/ruby/gems/1.8"
    export GEM_HOME="/home/myuser/gems"
    export PATH="/home/myuser/gems/bin:${PATH}"
    

    This will tell RubyGems to install gems in the /home/myuser/gems directory, and to use Gems’ binaries from your local installation first.
    Eventually create the /home/myuser/gems directory.
    Then edit RubyGems configuration file to set Gem files installation local (this will be useful to be used using bundler later):

    ---
    gem: --local --run-tests
    gemhome: /home/myuser/gems
    gempath: []
    
    rdoc: --inline-source --line-numbers
    
  2. Install Rails3:
    gem install rails
    

    This will install all gems in your local /home/myuser/gems directory.

  3. Get your Rails3 application code in a local directory, or create a new one using rails new myapp in a directory. In my tutorial, the app is in /home/myuser/rails_apps/myapp.
  4. Install all gems needed by your Rails3 application using bundler from your application directory (/home/myuser/rails_apps/myapp):
    bundle install
    
  5. Setup your Rails3 application: database connection, configuration…
  6. Test your application by running it using the default webrick server:
    rails s -e production -p 12006
    

    You should be able to see your Rails application running using top (in another terminal):

    > top -u myuser -c
    top - 12:52:21 up 64 days,  9:37,  2 users,  load average: 2.47, 2.54, 2.64
    Tasks: 310 total,   2 running, 307 sleeping,   0 stopped,   1 zombie
    Cpu(s): 11.4%us,  2.7%sy,  0.6%ni, 77.8%id,  7.3%wa,  0.0%hi,  0.1%si,  0.0%st
    Mem:  10376292k total,  8649296k used,  1726996k free,   432028k buffers
    Swap:  5406712k total,     1220k used,  5405492k free,  5260176k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    26747 myuser    21   0  2596 1096  732 R  1.9  0.0   0:00.02 top -u myuser -c
    25523 myuser    15   0 33212  26m 3872 S  0.0  0.3   0:01.69 /usr/bin/ruby script/rails s -e production -p 12006
    

    You should also be able to see your Rails3 application running at your external URL, on port 12006 (for example http://my_public_url.com:12006).
    Once this is checked, you can stop your Rails3 webrick server. We will now use nginx and Unicorn to make it running.

nginx setup

  1. Install nginx in a local directory, grabbing it first from nginx downloads (check nginx download page to get the latest version URL.
    mkdir ~/nginx
    cd ~/nginx
    wget http://nginx.org/download/nginx-1.2.7.tar.gz
    tar xvzf nginx-1.2.7.tar.gz
    cd nginx-1.2.7
    ./configure --prefix=/home/myuser/nginx/nginx-1.2.7-install --user=myuser --group=mygroup --with-http_ssl_module
    make
    make install
    

    This has installed nginx in directory /home/myuser/nginx/nginx-1.2.7-install

  2. Modify nginx configuration file for it to run on a port >1024.
        keepalive_timeout  65;
    
        #gzip  on;
    
        server {
            listen       12006;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
  3. Test nginx works correctly by running it from its installation directory /home/myuser/nginx/nginx-1.2.7-install:
    ./sbin/nginx
    

    Now you should see a master and worker processes running using top:

    > top -u myuser -c
    top - 12:31:02 up 64 days,  9:16,  2 users,  load average: 3.17, 2.80, 2.67
    Tasks: 314 total,   4 running, 310 sleeping,   0 stopped,   0 zombie
    Cpu(s): 16.2%us, 22.0%sy,  0.0%ni, 39.0%id, 22.6%wa,  0.0%hi,  0.2%si,  0.0%st
    Mem:  10376292k total,  8240012k used,  2136280k free,   472592k buffers
    Swap:  5406712k total,     1220k used,  5405492k free,  4849432k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
     5033 myuser    18   0  2600 1240  836 R  0.7  0.0   0:00.03 top -u myuser -c
    12718 myuser    25   0  5860  700  336 S  0.0  0.0   0:00.00 nginx: master process ./sbin/nginx
    12720 myuser    15   0  6084 1136  568 S  0.0  0.0   0:00.00 nginx: worker process
    

    You can already access your server externally using its port (here: 12006): you should see nginx welcoming page (for example at http://my_public_url.com:12006).
    nginx
    Once this is checked, you can shutdown nginx server by issuing:

    ./sbin/nginx -s stop
    

Unicorn setup

  1. Add the unicorn gem to your Rails application’s Gemfile
    source 'https://rubygems.org'
    gem 'rails', '3.2.12'
    gem 'unicorn'
    
  2. Install the unicorn gem from your application’s directory:
    bundle install
    
  3. Create the Unicorn configuration file in your Rails’ application config/unicorn.rb (adapt it to your needs in highlighted lines):
    # config/unicorn.rb
    # Set environment to development unless something else is specified
    env = ENV["RAILS_ENV"] || "development"
    
    # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
    # documentation.
    worker_processes 4
    
    # listen on both a Unix domain socket and a TCP port,
    # we use a shorter backlog for quicker failover when busy
    listen "/home/myuser/rails_apps/myapp/tmp/my_site.socket", :backlog => 64
    
    # Preload our app for more speed
    preload_app true
    
    # nuke workers after 30 seconds instead of 60 seconds (the default)
    timeout 30
    
    pid "/home/myuser/rails_apps/myapp/tmp/unicorn.my_site.pid"
    
    # Production specific settings
    if env == "production"
      # Help ensure your application will always spawn in the symlinked
      # "current" directory that Capistrano sets up.
      working_directory "/home/myuser/rails_apps/myapp"
    
      # feel free to point this anywhere accessible on the filesystem
      user 'myuser', 'mygroup'
      shared_path = "/home/myuser/rails_apps/myapp"
    
      stderr_path "#{shared_path}/log/unicorn.stderr.log"
      stdout_path "#{shared_path}/log/unicorn.stdout.log"
    end
    
    before_fork do |server, worker|
      # the following is highly recomended for Rails + "preload_app true"
      # as there's no need for the master process to hold a connection
      if defined?(ActiveRecord::Base)
        ActiveRecord::Base.connection.disconnect!
      end
    
      # Before forking, kill the master process that belongs to the .oldbin PID.
      # This enables 0 downtime deploys.
      old_pid = "/home/myuser/rails_apps/myapp/tmp/unicorn.my_site.pid.oldbin"
      if File.exists?(old_pid) && server.pid != old_pid
        begin
          Process.kill("QUIT", File.read(old_pid).to_i)
        rescue Errno::ENOENT, Errno::ESRCH
          # someone else did our job for us
        end
      end
    end
    
    after_fork do |server, worker|
      # the following is *required* for Rails + "preload_app true",
      if defined?(ActiveRecord::Base)
        ActiveRecord::Base.establish_connection
      end
    
      # if preload_app is true, then you may also want to check and
      # restart any other shared sockets/descriptors such as Memcached,
      # and Redis.  TokyoCabinet file handles are safe to reuse
      # between any number of forked children (assuming your kernel
      # correctly implements pread()/pwrite() system calls)
    end
    
  4. Check that your application runs correctly on Unicorn by starting it from your rails application directory:
    unicorn_rails -c config/unicorn.rb -E production -D -p 12006
    

    You should now see 5 new processes in your top: the unicorn master and 5 workers:

    > top -u myuser -c
    top - 17:21:53 up 64 days, 14:06,  2 users,  load average: 1.20, 1.19, 1.36
    Tasks: 306 total,   2 running, 303 sleeping,   0 stopped,   1 zombie
    Cpu(s): 11.4%us,  2.7%sy,  0.6%ni, 77.8%id,  7.3%wa,  0.0%hi,  0.1%si,  0.0%st
    Mem:  10376292k total,  9823156k used,   553136k free,   419012k buffers
    Swap:  5406712k total,     1220k used,  5405492k free,  6433556k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    19318 myuser    18   0  2596 1160  760 R  1.9  0.0   0:00.01 top -u myuser -c
    14518 myuser    15   0 39800  33m 3940 S  0.0  0.3   0:01.80 unicorn_rails master -c config/unicorn.rb -E production -D -p 12006
    14564 myuser    18   0 39884  31m 2320 S  0.0  0.3   0:00.09 unicorn_rails worker[0] -c config/unicorn.rb -E production -D -p 12006
    14565 myuser    15   0 39808  31m 2276 S  0.0  0.3   0:00.07 unicorn_rails worker[1] -c config/unicorn.rb -E production -D -p 12006
    14566 myuser    15   0 39800  30m 1732 S  0.0  0.3   0:00.01 unicorn_rails worker[2] -c config/unicorn.rb -E production -D -p 12006
    14567 myuser    15   0 39808  31m 2096 S  0.0  0.3   0:00.05 unicorn_rails worker[3] -c config/unicorn.rb -E production -D -p 12006
    

    And now you should again be able to see your Rails application served by Unicorn using its external URL http://my_public_url.com:12006.
    Then you can shut down your Unicorn server by using the PID of the master process (1234 in my example), and sending the QUIT signal:

    kill -QUIT 1234
    

Unicorn setup with nginx

Now that your Rails application can execute in Unicorn and you have a valid nginx setup, it is time to configure them to put Unicorn behind nginx.
You may have noticed in our Unicorn configuration that the Unicorn server also listens to a socket Unix file (/home/myuser/rails_apps/myapp/tmp/my_site.socket). Time for nginx to route requests to this socket file.

  1. Update nginx configuration file to send requests to the Unix socket. This is done by adding the upstream section:
        #gzip  on; 
     
        # this can be any application server, not just Unicorn/Rainbows!
        upstream app_server {
          # fail_timeout=0 means we always retry an upstream even if it failed
          # to return a good HTTP response (in case the Unicorn master nukes a
          # single worker for timing out).
       
          # for UNIX domain socket setups:
          server unix:/home/myuser/rails_apps/myapp/tmp/my_site.socket fail_timeout=0;
       
          # for TCP setups, point these to your backend servers
          # server 192.168.0.7:8080 fail_timeout=0;
          # server 192.168.0.8:8080 fail_timeout=0;
          # server 192.168.0.9:8080 fail_timeout=0;
        }
     
        server {
            listen       12006;
    
  2. Update nginx configuration file to server static files from your Rails’ public directory. This is done by removing the / location and adding a root directive:
        server {
            listen       12006;
            server_name  my_public_url.com;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            # Comment out "location /" section
            #location / {
            #    root   html;
            #    index  index.html index.htm;
            #}
    
            # Serve static files from the Rails application
            root /home/myuser/rails_apps/myapp/public;
    
            #error_page  404              /404.html;
    
  3. Update nginx configuration file to server all URI to our application server. This is done by adding a location section handling all URIs:
            root /home/xaeoncom/rails_apps/testr3/public;
    
            # Prefer to serve static files directly from nginx to avoid unnecessary
            # data copies from the application server.
            #
            # try_files directive appeared in in nginx 0.7.27 and has stabilized
            # over time. Older versions of nginx (e.g. 0.6.x) requires
            # "if (!-f $request_filename)" which was less efficient:
            # http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
            try_files $uri/index.html $uri.html $uri @app;
    
            location @app {
              # an HTTP header important enough to have its own Wikipedia entry:
              # http://en.wikipedia.org/wiki/X-Forwarded-For
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
              # enable this if you forward HTTPS traffic to unicorn,
              # this helps Rack set the proper URL scheme for doing redirects:
              # proxy_set_header X-Forwarded-Proto $scheme;
    
              # pass the Host: header from the client right along so redirects
              # can be set properly within the Rack application
              proxy_set_header Host $http_host;
    
              # we don't want nginx trying to do something clever with
              # redirects, we set the Host: header above already.
              proxy_redirect off;
    
              # set "proxy_buffering off" *only* for Rainbows! when doing
              # Comet/long-poll/streaming. It's also safe to set if you're using
              # only serving fast clients with Unicorn + nginx, but not slow
              # clients. You normally want nginx to buffer responses to slow
              # clients, even with Rails 3.1 streaming because otherwise a slow
              # client can become a bottleneck of Unicorn.
              #
              # The Rack application may also set "X-Accel-Buffering (yes|no)"
              # in the response headers do disable/enable buffering on a
              # per-response basis.
              # proxy_buffering off;
    
              proxy_pass http://app_server;
            }
    
            #error_page  404              /404.html;
    
  4. Start your nginx server from its installation directory:
    ./sbin/nginx
    
  5. Start your Unicorn server from your Rails application directory, with no port specified (it will use the Unix socket from its configuration instead):
    unicorn_rails -E production -D -c config/unicorn.rb
    

You can find a complete nginx.conf file example here.

And now you should be all set.

You should have master and worker processes for both nginx and unicorn:

> top -u myuser -c
top - 20:16:39 up 64 days, 17:01,  3 users,  load average: 1.45, 1.12, 1.15
Tasks: 295 total,   1 running, 293 sleeping,   0 stopped,   1 zombie
Cpu(s): 12.8%us,  1.6%sy,  0.0%ni, 69.3%id, 16.1%wa,  0.0%hi,  0.2%si,  0.0%st
Mem:  10376292k total, 10123176k used,   253116k free,   439456k buffers
Swap:  5406712k total,     1220k used,  5405492k free,  7523664k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
30744 myuser    15   0  2556 1184  808 R  0.3  0.0   0:14.72 top -u myuser -c
  631 myuser    15   0 39788  33m 3892 S  0.0  0.3   0:01.67 unicorn_rails master -E production -D -c config/unicorn.rb
  660 myuser    15   0 39788  30m 1760 S  0.0  0.3   0:00.01 unicorn_rails worker[0] -E production -D -c config/unicorn.rb
  661 myuser    15   0 39788  30m 1760 S  0.0  0.3   0:00.01 unicorn_rails worker[1] -E production -D -c config/unicorn.rb
  662 myuser    18   0 39868  31m 2332 S  0.0  0.3   0:00.05 unicorn_rails worker[2] -E production -D -c config/unicorn.rb
  663 myuser    15   0 39868  31m 2336 S  0.0  0.3   0:00.07 unicorn_rails worker[3] -E production -D -c config/unicorn.rb
 8612 myuser    25   0  6000  704  336 S  0.0  0.0   0:00.00 nginx: master process ./sbin/nginx
 8615 myuser    15   0  6152 1196  620 S  0.0  0.0   0:00.00 nginx: worker process

And your Rails application should be accessible using its external URL: http://my_public_url.com:12006, running nginx, forwarding requests to unicorn.

About Muriel Salvan

I am a freelance project manager and polyglot developer, expert in Ruby and Rails. I created X-Aeon Solutions and rivierarb Ruby meetups. I also give trainings and conferences on technical topics. My core development principles: Plugins-oriented architectures, simple components, Open Source power, clever automation, constant technology watch, quality and optimized code. My experience includes big and small companies. I embrace agile methodologies and test driven development, without giving up on planning and risks containment methods as well. I love Open Source and became a big advocate.
Howto, Ruby on Rails, Web development , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *