Skip to content

Instantly share code, notes, and snippets.

Created July 6, 2013 20:54
Show Gist options
  • Save JamesDullaghan/5941259 to your computer and use it in GitHub Desktop.
Save JamesDullaghan/5941259 to your computer and use it in GitHub Desktop.
Deploy rails app to digitalocean with nginx, unicorn, capistrano & postgres

Deploy Rails app to digitalocean with nginx, unicorn, capistrano & postgres

Create droplet of your liking (ubuntu 12.10 x32)

ssh to root in terminal with your server ip

ssh root@

Add ssh fingerprint and enter password provided in email

Change password


Create new user

adduser username

Set new users privileges


Find user privileges section

# User privilege specification

Add your new user privileges under root & cntrl+x then y to save

username ALL=(ALL:ALL) ALL

Configure SSH

nano /etc/ssh/sshd_config

Find and change port to one that isn't default(22 is default: choose between 1025 ..65536)

Port 22 # change this to whatever port you wish to use
Protocol 2
PermitRootLogin no

Add to bottom of sshd_config file after changing port (cntrl+x then y to save)

UseDNS no
AllowUsers username

Reload ssh

reload ssh

Don't close root! Open new shell and ssh to vps with new username(remember the port, or you're locked out!)

ssh -p 1026 username@

Update packages on virtual server

sudo apt-get update
sudo apt-get install curl

install latest stable version of rvm

curl -L | bash -s stable

load rvm

source ~/.rvm/scripts/rvm

install rvm dependencies

rvm requirements

Install ruby 2.0.0

rvm install 2.0.0

Use 2.0.0 as rvm default

rvm use 2.0.0 --default

install latest version of rubygems if rvm install didn't

rvm rubygems current

install rails gem

gem install rails --no-ri --no-rdoc

Install postgres

sudo apt-get install postgresql postgresql-server-dev-9.1
gem install pg -- --with-pg-config=/usr/bin/pg_config

Create new postgres user

sudo -u postgres psql
create user username with password 'password';
alter role username superuser createrole createdb replication;
create database projectname_production owner username;

Install git-core

sudo apt-get install git-core

Install bundler

gem install bundler

setup nginx

sudo apt-get install nginx
nginx -h
cat /etc/init.d/nginx
/etc/init.d/nginx -h
sudo service nginx start
cd /etc/nginx

local unicorn setup

Add unicorn to the gemfile
create unicorn.rb & file
chmod +x config/

nginx.conf (change projectname and username to match your directory structure!) upstream unicorn { server unix:/tmp/unicorn.projectname.sock fail_timeout=0; }

server {
  listen 80 default deferred;
  # server_name;
  root /home/username/apps/projectname/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;

  try_files $uri/index.html $uri @unicorn;
  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;


root = "/home/username/apps/projectname/current"
working_directory root
pid "#{root}/tmp/pids/"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

listen "/tmp/unicorn.projectname.sock"
worker_processes 2
timeout 30

# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
  ENV["BUNDLE_GEMFILE"] = File.join(root, 'Gemfile')


# Provides:          unicorn
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Manage unicorn server
# Description:       Start, stop, restart unicorn server for a specific application.
set -e

# Feel free to change any of the following variables for your app:
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
set -u


sig () {
  test -s "$PID" && kill -$1 `cat $PID`

oldsig () {
  test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`

run () {
  if [ "$(id -un)" = "$AS_USER" ]; then
    eval $1
    su -c "$1" - $AS_USER

case "$1" in
  sig 0 && echo >&2 "Already running" && exit 0
  run "$CMD"
  sig QUIT && exit 0
  echo >&2 "Not running"
  sig TERM && exit 0
  echo >&2 "Not running"
  sig HUP && echo reloaded OK && exit 0
  echo >&2 "Couldn't reload, starting '$CMD' instead"
  run "$CMD"
  if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
    while test -s $OLD_PIN && test $n -ge 0
      printf '.' && sleep 1 && n=$(( $n - 1 ))

    if test $n -lt 0 && test -s $OLD_PIN
      echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
      exit 1
    exit 0
  echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  run "$CMD"
  sig USR1
  echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
  exit 1

Add capistrano and rvm capistrano to gemfile

gem 'capistrano'
gem 'rvm-capistrano'

Create capfile & config/deploy.rb files

capify .


require "bundler/capistrano"
require "rvm/capistrano"

server "", :web, :app, :db, primary: true

set :application, "projectname"
set :user, "username"
set :port, 22
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false

set :scm, "git"
set :repository, "{application}.git"
set :branch, "master"

default_run_options[:pty] = true
ssh_options[:forward_agent] = true

after "deploy", "deploy:cleanup" # keep only the last 5 releases

namespace :deploy do
  %w[start stop restart].each do |command|
    desc "#{command} unicorn server"
    task command, roles: :app, except: {no_release: true} do
      run "/etc/init.d/unicorn_#{application} #{command}"

  task :setup_config, roles: :app do
    sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
    sudo "ln -nfs #{current_path}/config/ /etc/init.d/unicorn_#{application}"
    run "mkdir -p #{shared_path}/config"
    put"config/database.example.yml"), "#{shared_path}/config/database.yml"
    puts "Now edit the config files in #{shared_path}."
  after "deploy:setup", "deploy:setup_config"

  task :symlink_config, roles: :app do
    run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  after "deploy:finalize_update", "deploy:symlink_config"

  desc "Make sure local git is in sync with remote."
  task :check_revision, roles: :web do
    unless `git rev-parse HEAD` == `git rev-parse origin/master`
      puts "WARNING: HEAD is not the same as origin/master"
      puts "Run `git push` to sync changes."
  before "deploy", "deploy:check_revision"


load 'deploy'
load 'deploy/assets'
load 'config/deploy'

Shake hands with github

# follow the steps in this guide if receive permission denied(public key)

Add ssh key to digitalocean

cat ~/.ssh/ | ssh -p 22 username@ 'cat >> ~/.ssh/authorized_keys'

Create repo and push to github

# Add config/database.yml to .gitignore
cp config/database.yml config/database.example.yml
git init
git add .
git commit -m "Inital Commit"
git remote add origin
git push origin master


cap deploy:setup
# edit /home/username/apps/projectname/shared/config/database.yml on server
cap deploy:cold

after deploy:cold

sudo rm /etc/nginx/sites-enabled/default
sudo service nginx restart
sudo update-rc.d -f unicorn_projectname defaults

Make changes and deploy

# Make changes
git add .
git commit -m "Changes"
git push origin master
cap deploy

Resources from Railscasts/digital ocean documentation. For use if puppet or chef is a little over your head. I know you can spawn up a rails app using nginx and unicorn with mysql, but you don't learn much that way!

Hopefully I didn't miss any steps, although I'm sure I did. Please leave comments if you run into troubles.

Copy link

mcavallo commented Aug 9, 2013

Looks great, and you even changed the ssh port :)
Gonna give this a go as soon as I can.

Copy link

If you are ever wondering why your CSS is loaded but no styles are applied in your application.
Add include mime.types; to the http section of nginx.

Copy link

ilyazub commented Oct 30, 2013

Thank you for sharing this information :-)

Copy link

mer10z commented Nov 2, 2013

You might want to add something about modifying config/initializers/secret_token.rb to use an env var before pushing to github.

Copy link

beautiful thanks.

Copy link

dmix commented Dec 29, 2013

Capistrano config code is outdated now with the release of version 3, so you can downgrade here to keep this code working:

gem 'capistrano', '2.15.5'

Copy link

Thanks this is great. but for some reason after everything is setup when I visit the ip in the browser it redirects to a randome page ( for ip Where would this be defined? I am thinking my nginx.conf is wrong, would you mind posting the whole file?

Copy link

of6 commented Apr 1, 2014

Is there an update for using Capistrano 3?

Copy link

Great post, thank you very much.

Copy link

Thanks. may use it later

Copy link

Thank you!

Copy link

Copy link

You might want to add something about modifying config/initializers/secret_token.rb to use an env var before pushing to github. [2]

Copy link

Thank you man!

Copy link

alexonozor commented Oct 25, 2016

Hi man, my Nginx has refused to start. when I run restart it fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment