Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
DigitalOcean Ubuntu 14.04 x64 + Rails 4 + Nginx + Unicorn + PostgreSQL + Capistrano 3 Setup Instructions

DigitalOcean Ubuntu 14.04 x64 + Rails 4 + Nginx + Unicorn + PostgreSQL + Capistrano 3

Create a Droplet

SSH into Root

$ ssh root@

Change Root Password

$ passwd

Add Deploy User

$ adduser deployer

Update Sudo Privileges

$ visudo
username ALL=(ALL:ALL) ALL

Configure SSH

$ vi /etc/ssh/sshd_config
Port 22 # Change (1025..65536)
Protocol 2 # Change
PermitRootLogin no  # Change
UseDNS no # Add
AllowUsers deployer # Add

Reload SSH

$ reload ssh

SSH with Deploy User (Don't close root)

$ ssh -p 1026 deployer@

Install Curl

$ sudo apt-get update
$ sudo apt-get install curl

Install RVM

$ curl -L | bash -s stable
$ source ~/.rvm/scripts/rvm
$ rvm requirements
$ rvm install 2.2.0
$ rvm use 2.2.0 --default
$ rvm rubygems current

Install PostgreSQL

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

Create Postgres User

$ sudo -u postgres psql
create user deployer with password 'password';
alter role deployer superuser createrole createdb replication;
create database MYAPP_production owner deployer;

Install GIT

$ 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
$ sudo vim /etc/nginx/sites-enabled/default

upstream unicorn {
  server unix:/tmp/unicorn.MYAPP.sock fail_timeout=0;

server {
  listen 80 default deferred;
  # server_name;
  root /home/deployer/apps/MYAPP/current/public;

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

  location ~ ^/(robots.txt|sitemap.xml.gz)/ {
    root /home/deployer/apps/MYAPP/current/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;

Add Unicorn

$ vim Gemfile
gem 'unicorn'    

Update Unicorn Config

$ vim config/unicorn/production.rb

root = "/home/deployer/apps/MYAPP/current"
working_directory root

pid "#{root}/tmp/pids/"

stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

worker_processes Integer(ENV['WEB_CONCURRENCY'])
timeout 30
preload_app true

listen '/tmp/unicorn.spui.sock', backlog: 64

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT',

  defined?(ActiveRecord::Base) and

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'

  defined?(ActiveRecord::Base) and

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

Add Capistrano

$ vim Gemfile

group :development do
  gem 'capistrano-rails'
  gem 'capistrano-rvm'
  gem 'capistrano3-unicorn'

Install Capistrano

$ bundle exec cap install

Update Capistrano Capfile

$ vim Capfile

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

# Load custom tasks from `lib/capistrano/tasks' if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Update Capistrano Deploy Config

$ vim config/deploy.rb

lock '3.3.5'

set :application, 'spui'
set :repo_url, ''

ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call

set :use_sudo, false
set :bundle_binstubs, nil
set :linked_files, fetch(:linked_files, []).push('config/database.yml')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

after 'deploy:publishing', 'deploy:restart'

namespace :deploy do
  task :restart do
    invoke 'unicorn:reload'

Update Production Deploy Config

$ vim config/deploy/production.rb

set :port, 22
set :user, 'deployer'
set :deploy_via, :remote_cache
set :use_sudo, false

server '123.333.333.333',
  roles: [:web, :app, :db],
  port: fetch(:port),
  user: fetch(:user),
  primary: true

set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"

set :ssh_options, {
  forward_agent: true,
  auth_methods: %w(publickey),
  user: 'deployer',

set :rails_env, :production
set :conditionally_migrate, true    

Add SSH Key to DigitalOcean

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

Say Hi to Github

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

Check Deployment (Commit and Push)

$ cap production deploy:check


$ cap production deploy

Thanks to James Dullaghan for his help.

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.