Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Ruby on Rails server setup on Ubuntu 11.04 with Nginx, Unicorn, Rbenv
#! /bin/bash
# Provides: unicorn
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn web server
# Description: starts unicorn
DAEMON_OPTS="-c /home/$USER/website/config/unicorn.rb -E production -D"
DESC="Unicorn app for $USER"
case "$1" in
CD_TO_APP_DIR="cd /home/$USER/website"
echo -n "Starting $DESC: "
if [ `whoami` = root ]; then
su - $USER -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS"
$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS
echo "$NAME."
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
echo -n "Restarting $DESC: "
kill -USR2 `cat $PID`
echo "$NAME."
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
exit 0
upstream example-workers {
# 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).
server unix:/tmp/ fail_timeout=0;
server {
listen 80; # default;
root /home/;
location / {
access_log off;
include proxy_params;
proxy_redirect off;
if (-f $request_filename) {
access_log off;
expires max;
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
if (!-f $request_filename) {
proxy_pass http://example-workers;

Server Commissioning

Ubuntu mainstream packages are pretty out of date for nginx; we want version > 1.0, so we need to reference repository that has more recent versions before we install:

$ add-apt-repository ppa:nginx/stable && apt-get update

Update, upgrade and install nginx and development tools:

$ apt-get -y install nginx git-core build-essential

Extras for RubyGems and Rails:

$ apt-get -y install zlib1g-dev
$ apt-get -y install libssl-dev libsqlite3-dev
$ apt-get -y install libreadline5-dev
$ apt-get -y install curl

Add a deployment user:

$ useradd -m -g staff -s /bin/bash deployer
$ passwd deployer

Create a custom shudders file, and add the following line (sudo vi /etc/sudoers.d/our-company):

%staff ALL=(ALL) ALL


Edit /etc/nginx/proxy_params and add shared proxy config settings (optional)

proxy_set_header Host $host;

# needed to forward user's IP address to application server
proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Proto $scheme;

proxy_send_timeout         90;
proxy_read_timeout         90;

proxy_buffer_size          4k;
proxy_buffers              4 32k;
proxy_busy_buffers_size    64k;
proxy_temp_file_write_size 64k;

Ruby & Rails Setup ror each User / App

$ sudo adduser --shell /bin/bash example-user
$ su - example-user
$ cd ~example-user

Install rbenv:

Check out rbenv into ~/.rbenv :

$ git clone git:// .rbenv

Add ~/.rbenv/bin to your $PATH for access to the rbenv command-line

$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>   
$  ~/.bash_profile

Add rbenv init to your shell to enable shims and autocompletion:

$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

Restart your shell so the path changes take effect in order to use rbenv:

$ exec $SHELL

If the above shell reload doesn't give you the rbenv command, then you will have to exit and re-enter the shell

Use rbenv to install a specific Ruby version

$ rbenv install 1.9.2-p290

Rebuild the shim binaries. You should do this any time you install a new Ruby binary e.g. when installing a new Ruby version, or when installing a gem that provides a binary:

$ rbenv rehash

Set a global Ruby version for all shells:

$ rbenv global 1.9.2-p290


$ gem install bundler unicorn --no-rdoc --no-ri; rbenv rehash

Add the following environment config variables to a file at /etc/unicorn/ (The Unicorn process will look here):


Clone your app into ~/website (depends upon if you are using git, or some other source), and then install your bundle:

$ cd ~/website
$ bundle install

create app-specific unicorn init file here and make it executable (See init file in this gist):

$ sudo chmod +x /etc/init.d/  

Add a unicorn.rb app config file to your Rails app at config/unicorn.rb (Example attached). Edit the file to match the directory path to your app, and user names and groups. Also uncomment the 'listen' directive in the file for listening on a TCP port.

Check that unicorn can be started:

$ /etc/init.d/ start

Check that unicorn is listening on the configured port (8080 in this example):

$ netstat -natp | grep unicorn

You should see something like:

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0  *               LISTEN      5272/unicorn.rb -E 

Okay, if that's all good, then you can comment out the TCP port listening directive in config/unicorn.rb, so that unicorn worker processes are only accessible to Nginx via Unix socket.

Nginx virtual hosts

Create an Nginx virtual hosts configuration file in 'sites-available' and enter contents of nginx_virtual_host file:

$ sudo vi /etc/nginx/sites-available/

Create a symlink from sites-available to site-enabled:

$ sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/
# See for complete documentation
# This file should go in the config directory of your Rails app e.g. config/unicorn.rb
app_dir = "/home/"
worker_processes 5
working_directory app_dir
# Load app into the master before forking workers for super-fast
# worker spawn times
preload_app true
# nuke workers after 60 seconds (the default)
timeout 60
# listen on a Unix domain socket and/or a TCP port,
#listen 8080 # listen to port 8080 on all TCP interfaces
#listen "" # listen to port 8080 on the loopback interface
listen "/tmp/"
# feel free to point this anywhere accessible on the filesystem
user '', ''
pid "#{app_dir}/pids/"
stderr_path "#{app_dir}/log/unicorn.stderr.log"
stdout_path "#{app_dir}/log/unicorn.stdout.log"
if GC.respond_to?(:copy_on_write_friendly=)
GC.copy_on_write_friendly = true
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
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
# When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
# immediately start loading up a new version of itself (loaded with a new
# version of our app). When this new Unicorn is completely loaded
# it will begin spawning workers. The first worker spawned will check to
# see if an .oldbin pidfile exists. If so, this means we've just booted up
# a new Unicorn and need to tell the old one that it can now die. To do so
# we send it a QUIT.
# Using this method we get 0 downtime deploys.
old_pid = "#{server.config[:pid]}.oldbin"
if File.exists?(old_pid) && != old_pid
sig = ( + 1) >= server.worker_processes ? :QUIT : :TTOU
rescue Errno::ENOENT, Errno::ESRCH
# someone else did our job for us
after_fork do |server, worker|
# Unicorn master loads the app then forks off workers - because of the way
# Unix forking works, we need to make sure we aren't using any of the parent's
# sockets, e.g. db connection
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
# Redis and Memcached would go here but their connections are established
# on demand, so the master never opens a socket

This comment has been minimized.

Copy link

@vsizov vsizov commented Mar 7, 2012

root@tool:~# /etc/init.d/redmine start
Starting Unicorn app for redmine: -su: bundle: command not found


but if run script from user redmine then work fine


This comment has been minimized.

Copy link

@inspire22 inspire22 commented Jan 28, 2014

Great init.d script for unicorn with bundle exec. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.