Skip to content

Instantly share code, notes, and snippets.

@websumy
Created October 7, 2015 08:35
Show Gist options
  • Save websumy/55dd1e358eb3be634db6 to your computer and use it in GitHub Desktop.
Save websumy/55dd1e358eb3be634db6 to your computer and use it in GitHub Desktop.
How to deploy rails app to digitalocean with nginx, unicorn, capistrano & postgres

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

Server setup

Create droplet of your liking (ubuntu 14.04 x64)

ssh to root in terminal with your server ip

ssh root@123.123.123.123

Create new user

adduser deploy

Set new users privileges

visudo

Find user privileges section

# User privilege specification
root  ALL=(ALL:ALL) ALL

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

deploy ALL=(ALL:ALL) ALL

Open new shell and ssh to vps with deploy user.

ssh deploy@123.123.123.123

Update packages on virtual server

sudo apt-get update

install latest stable version of rvm

curl -L get.rvm.io | bash -s stable

follow instruction to check signature

command curl -sSL https://rvm.io/mpapis.asc | gpg --import -

try once again to ro install rvm

curl -L get.rvm.io | bash -s stable

load rvm

source ~/.rvm/scripts/rvm

install rvm dependencies

rvm requirements

Install ruby version 2.2.2

rvm install 2.2.2

also create gemset list

rvm use 2.2.2@projectname --create

Install bundler

gem install bundler

Install postgres

sudo apt-get install postgresql postgresql-server-dev-9.3

Create new postgres user

sudo -u postgres psql

After you run PostgreSQL comand line console you should run the following (line by line):

create user username with password 'password';
alter role username superuser createrole createdb replication;
create database projectname_production owner username;

(user \q + enter to exit from psql console)

Also change peer authentication to md5

sudo nano /etc/postgresql/9.3/main/pg_hba.conf

should be:

local   all             all                                     md5

Restart postgresql

sudo service postgresql restart

Install git-core

sudo apt-get install git-core

Install NodeJs (optional)

sudo apt-get install nodejs

Setup nginx

sudo apt-get install nginx
sudo service nginx start

Add nginx config

/etc/nginx/sites-available/default

upstream app_server {
    server unix:/home/deploy/www/projectname/shared/unicorn.sock;
}

server {
    client_max_body_size 200M;
    listen 80;
    server_name 123.123.123.123;

    location ~ ^/api {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server;
    }

    location / {
        root /home/deploy/www/projectname/current/public;
        try_files $uri /assets/pages/index.htm;
        #try_files $uri /500.html;
        index index.htm;
    }

    error_page 500 502 503 504 /500.html;
    keepalive_timeout 10;
}

Also don't forget to reload nginx

sudo service nginx reload

Create config/database.yml & .env files:

/home/deploy/www/projectname/shared/.env

SECRET_KEY_BASE=some_secret_key
PROTOCOL=http
HOST=123.123.123.123

/home/deploy/www/projectname/shared/config/database.yml

production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: projectname_production
  username: username
  password: password

Generate SSH keys (without password) and add public key to github repository

ssh-keygen -t rsa 
cat ~/.ssh/id_rsa.pub

Copy and paste it in your project https://github.com/Rezonans/PROJECTNAME/settings/keys

If you are going to use ImageMagick you should install it

sudo apt-get install imagemagick

Local setup

Add unicorn to the gemfile

gem 'unicorn', group: :production

Create unicorn config file (config/unicorn/staging.rb)

# Set the working application directory
working_directory '/home/deploy/www/projectname/current'

# Unicorn PID file location
pid '/home/deploy/www/projectname/current/tmp/pids/unicorn.pid'

# Path to logs
stderr_path '/home/deploy/www/projectname/current/log/unicorn.log'
stdout_path '/home/deploy/www/projectname/current/log/unicorn.log'

# Unicorn socket
listen '/home/deploy/www/projectname/shared/unicorn.sock'

# Number of processes
worker_processes 2

# Time-out
timeout 30

Add capistrano gems to gemfile

group :development
  gem 'capistrano', '3.4.0'
  gem 'capistrano-rvm'
  gem 'capistrano-rails', '1.1.3'
  gem 'capistrano3-unicorn'
end

Create Capfile & config/deploy.rb & config/deploy/staging.rb files

Capfile

# Load DSL and set up stages
require 'capistrano/setup'

# Include default deployment tasks
require 'capistrano/deploy'

# Include tasks from other gems included in your Gemfile
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rails'
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 }

config/deploy.rb

# config valid only for current version of Capistrano
lock '3.4.0'

set :application, 'projectname'
set :repo_url, 'git@github.com:Rezonans/projectname.git'

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

# Default value for :scm is :git
set :scm, :git

# Default value for :format is :pretty
set :format, :pretty

# Default value for :log_level is :debug
# set :log_level, :debug

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
set :linked_files, fetch(:linked_files, []).push('.env', 'config/database.yml')


# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for keep_releases is 5
# set :keep_releases, 5

desc 'Invoke a rake command on the remote server'
task :invoke, [:command] => 'deploy:set_rails_env' do |task, args|
  on primary(:app) do
    within current_path do
      with :rails_env => fetch(:rails_env) do
        rake args[:command]
      end
    end
  end
end

namespace :deploy do
  after :restart, :clear_cache do
    invoke 'unicorn:reload'
    on roles(:web), in: :groups, limit: 3, wait: 10 do
    end
  end

  after :publishing, :restart
  after :finishing, :cleanup
  before :finishing, :restart
  after :rollback, :restart
end

namespace :rails do
  desc 'Open the rails console on each of the remote servers'
  task console: 'rvm:hook' do
    on roles(:app), primary: true do |host|
      execute_interactively host, 'console production'
    end
  end
end

def execute_interactively(host, command)
  command = "cd #{fetch(:deploy_to)}/current && #{SSHKit.config.command_map[:bundle]} exec rails #{command}"
  puts command if fetch(:log_level) == :debug
  exec "ssh -l #{host.user} #{host.hostname} -p #{host.port || 22} -t '#{command}'"
end

config/deploy/staging.rb

# Simple Role Syntax
# ==================
# Supports bulk-adding hosts to roles, the primary server in each group
# is considered to be the first unless any hosts have the primary
# property set.  Don't declare `role :all`, it's a meta role.

role :web, %w{deploy@123.123.123.123}
role :app, %w{deploy@123.123.123.123}

set :rvm_ruby_version, '2.2.2@projectname'
set :rvm_type, :user
set :branch, :master
set :rails_env, 'production'
set :unicorn_rack_env, 'production'
set :unicorn_config_path, -> { File.join(current_path, 'config', 'unicorn', 'staging.rb') }
set :deploy_to, '/home/deploy/www/projectname'

# Extended Server Syntax
# ======================
# This can be used to drop a more detailed server definition into the
# server list. The second argument is a, or duck-types, Hash and is
# used to set extended properties on the server.

server '123.123.123.123', user: 'deploy', roles: %w{web app}

Add ssh key to digitalocean

cat ~/.ssh/id_rsa.pub | ssh -p 22 deploy@123.123.123.123 'mkdir ~/.ssh/; cat >> ~/.ssh/authorized_keys'

Deploy

Run these commands from project root directory

cap staging deploy
cap staging invoke[db:migrate]
cap staging invoke[db:seed]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment