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.
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
- 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/gemsdirectory, and to use Gems’ binaries from your local installation first.
Eventually create the/home/myuser/gemsdirectory.
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
- Install Rails3:
gem install rails
This will install all gems in your local
/home/myuser/gemsdirectory. - Get your Rails3 application code in a local directory, or create a new one using
rails new myappin a directory. In my tutorial, the app is in/home/myuser/rails_apps/myapp. - Install all gems needed by your Rails3 application using bundler from your application directory (
/home/myuser/rails_apps/myapp):bundle install
- Setup your Rails3 application: database connection, configuration…
- 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
- 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 - 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; - 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).

Once this is checked, you can shutdown nginx server by issuing:./sbin/nginx -s stop
Unicorn setup
- Add the
unicorngem to your Rails application’s Gemfilesource 'https://rubygems.org' gem 'rails', '3.2.12' gem 'unicorn'
- Install the unicorn gem from your application’s directory:
bundle install
- 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 - 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.
- Update nginx configuration file to send requests to the Unix socket. This is done by adding the
upstreamsection:#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; - Update nginx configuration file to server static files from your Rails’ public directory. This is done by removing the / location and adding a
rootdirective: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; - Update nginx configuration file to server all URI to our application server. This is done by adding a
locationsection 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; - Start your nginx server from its installation directory:
./sbin/nginx
- 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.