Arquivo da tag: Deploy

Deploying a Rails App on Ubuntu 14.04 with Capistrano, Nginx, and Puma


Rails is an open source web application framework written in Ruby. It follows the Convention over Configuration philosophy by making assumptions that there is the ‘best’ way of doing things. This allows you to write less code while accomplishing more without having you go through endless config files.

Nginx is a high performance HTTP server, reverse proxy, and a load balancer known for its focus on concurrency, stability, scalability, and low memory consumption. Like Nginx, Puma is another extremely fast and concurrent web server with a very small memory footprint but built for Ruby web applications.

Capistrano is a remote server automation tool focusing mainly on Ruby web apps. It’s used to reliably deploy web apps to any number of remote machines by scripting arbitrary workflows over SSH and automate common tasks such as asset pre-compilation and restarting the Rails server.

In this tutorial we’ll install Ruby and Nginx on a DigitalOcean Ubuntu Droplet and configure Puma and Capistrano in our web app. Nginx will be used to capture client requests and pass them over to the Puma web server running Rails. We’ll use Capistrano to automate common deployment tasks, so every time we have to deploy a new version of our Rails app to the server, we can do that with a few simple commands.


To follow this tutorial, you must have the following:

  • Ubuntu 14.04 x64 Droplet
  • A non-root user named deploy with sudo privileges (Initial Server Setup with Ubuntu 14.04 explains how to set this up.)
  • Working Rails app hosted in a remote git repository that’s ready to be deployed

Optionally, for heightened security, you can disable root login via SSH and change the SSH port number as described in Initial Server Setup with Ubuntu 14.04.

Warning: After disabling root login, make sure you can SSH to your Droplet as the deploy user and use sudo for this user before closing the root SSH session you opened to make these changes.

All the commands in this tutorial should be run as the deploy user. If root access is required for the command, it will be preceded by sudo.

Step 1 — Installing Nginx

Once the VPS is secure, we can start installing packages. Update the package index files:

sudo apt-get update

Then, install Nginx:

sudo apt-get install curl git-core nginx -y

Step 2 — Installing Databases

Install the database that you’ll be using in your Rails app. Since there are lots of databases to choose from, we won’t cover them in this guide. You can see instructions for major ones here:

Also be sure to check out:

Step 3 — Installing RVM and Ruby

We won’t be installing Ruby directly. Instead, we’ll use a Ruby Version Manager. There are lots of them to choose from (rbenv, chruby, etc.), but we’ll use RVM for this tutorial. RVM allows you to easily install and manage multiple rubies on the same system and use the correct one according to your app. This makes life much easier when you have to upgrade your Rails app to use a newer ruby.

Before installing RVM, you need to import the RVM GPG Key:

gpg --keyserver hkp:// --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

Then install RVM to manage our Rubies:

curl -sSL | bash -s stable

This command uses curl to download the RVM Installation script from The -sSL option is composed of three flags:

  • -s tells curl to download the file in ‘silent mode’
  • -S tells curl to show an error message if it fails
  • -L tells curl to follow all HTTP redirects while retrieving the installation script

Once downloaded, the script is piped to bash. The -s option passes stable as an argument to the RVM Installation script to download and install the stable release of RVM.

Note: If the second command fails with the message “GPG signature verification failed”, that means the GPG Key has changed, simply copy the command from the error output and run it to download the signatures. Then run the curl command for the RVM Installation.

We need to load the RVM script (as a function) so we can start using it. We then need to run the requirements command to automatically install required dependencies and files for RVM and Ruby to function properly:

source ~/.rvm/scripts/rvm
rvm requirements

We can now install the Ruby of our choice. We’ll be installing the latest Ruby 2.2.1 (at the time of writing) as our default Ruby:

rvm install 2.2.1
rvm use 2.2.1 –default

Step 4 — Installing Rails and Bundler

Once Ruby is set up, we can start installing Rubygems. We’ll start by installing the Rails gem that will allow your Rails application to run, and then we’ll install bundler which can read your app’s Gemfile and automatically install all required gems.

To install Rails and Bundler:

gem install rails -V –no-ri –no-rdoc
gem install bundler -V –no-ri –no-rdoc

Three flags were used:

  • -V (Verbose Output): Prints detailed information about Gem installation
  • --no-ri – (Skips Ri Documentation): Doesn’t install Ri Docs, saving space and making installation fast
  • --no-rdoc – (Skips RDocs): Doesn’t install RDocs, saving space and speeding up installation

Note: You can also install a specific version of Rails according to your requirements by using the -v flag:

gem install rails -v 5.1.2 –no-ri –no-rdoc

Step 5 — Setting up SSH Keys

Since we want to set up smooth deployments, we’ll be using SSH Keys for authorization. First shake hands with GitHub, Bitbucket, or any other Git Remote where the codebase for your Rails app is hosted:

ssh -T
ssh -T

Don’t worry if you get a Permission denied (publickey) message. Now, generate a SSH key (a Public/Private Key Pair) for your server:

ssh-keygen -t rsa

Add the newly created public key (~/.ssh/ to your repository’s deployment keys:

If all the steps were completed correctly, you should now be able to clone your git repository (over the SSH Protocol, not HTTP) without entering your password:

git clone

If you need a sample app for testing, you can fork the following test app specifically created for this tutorial: Sample Rails App on GitHub

The git clone command will create a directory with the same name as your app. For example, a directory named testapp_rails will be created.

We are cloning only to check if our deployment keys are working, we don’t need to clone or pull our repository every time we push new changes. We’ll let Capistrano handle all that for us. You can now delete this cloned directory if you want to.

Open a terminal on your local machine. If you don’t have a SSH Key for your local computer, create one for it as well. In your local terminal session:

ssh-keygen -t rsa

Add your local SSH Key to your Droplet’s Authorized Keys file (remember to replace the port number with your customized port number):

cat ~/.ssh/ | ssh -p your_port_num deploy@your_server_ip ‘cat >> ~/.ssh/authorized_keys’

Step 6 — Adding Deployment Configurations in the Rails App

On your local machine, create configuration files for Nginx and Capistrano in your Rails application. Start by adding these lines to the Gemfile in the Rails App:

group :development do
    gem 'capistrano',         require: false
    gem 'capistrano-rvm',     require: false
    gem 'capistrano-rails',   require: false
    gem 'capistrano-bundler', require: false
    gem 'capistrano3-puma',   require: false
gem 'puma'

Use bundler to install the gems you just specified in your Gemfile. Enter the following command to bundle your Rails app:


After bundling, run the following command to configure Capistrano:

cap install

This will create:

  • Capfile in the root directory of your Rails app
  • deploy.rb file in the config directory
  • deploy directory in the config directory

Replace the contents of your Capfile with the following:

# Load DSL and Setup Up Stages
require 'capistrano/setup'
require 'capistrano/deploy'

require 'capistrano/rails'
require 'capistrano/bundler'
require 'capistrano/rvm'
require 'capistrano/puma'

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

This Capfile loads some pre-defined tasks in to your Capistrano configuration files to make your deployments hassle-free, such as automatically:

  • Selecting the correct Ruby
  • Pre-compiling Assets
  • Cloning your Git repository to the correct location
  • Installing new dependencies when your Gemfile has changed

Replace the contents of config/deploy.rb with the following, updating fields marked in red with your app and Droplet parameters:

# Change these
server 'your_server_ip', port: your_port_num, roles: [:web, :app, :db], primary: true

set :repo_url,        ''
set :application,     'appname'
set :user,            'deploy'
set :puma_threads,    [4, 16]
set :puma_workers,    0

# Don't change these unless you know what you're doing
set :pty,             true
set :use_sudo,        false
set :stage,           :production
set :deploy_via,      :remote_cache
set :deploy_to,       "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/"
set :puma_access_log, "#{release_path}/log/puma.error.log"
set :puma_error_log,  "#{release_path}/log/puma.access.log"
set :ssh_options,     { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/ }
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true  # Change to false when not using ActiveRecord

## Defaults:
# set :scm,           :git
# set :branch,        :master
# set :format,        :pretty
# set :log_level,     :debug
# set :keep_releases, 5

## Linked Files & Directories (Default None):
# set :linked_files, %w{config/database.yml}
# set :linked_dirs,  %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

namespace :puma do
  desc 'Create Directories for Puma Pids and Socket'
  task :make_dirs do
    on roles(:app) do
      execute "mkdir #{shared_path}/tmp/sockets -p"
      execute "mkdir #{shared_path}/tmp/pids -p"

  before :start, :make_dirs

namespace :deploy do
  desc "Make sure local git is in sync with remote."
  task :check_revision do
    on roles(:app) 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."

  desc 'Initial Deploy'
  task :initial do
    on roles(:app) do
      before 'deploy:restart', 'puma:start'
      invoke 'deploy'

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'puma:restart'

  before :starting,     :check_revision
  after  :finishing,    :compile_assets
  after  :finishing,    :cleanup
  after  :finishing,    :restart

# ps aux | grep puma    # Get puma pid
# kill -s SIGUSR2 pid   # Restart puma
# kill -s SIGTERM pid   # Stop puma

This deploy.rb file contains some sane defaults that work out-of-the-box to help you manage your app releases and automatically perform some tasks when you make a deployment:

  • Uses production as the default environment for your Rails app
  • Automatically manages multiple releases of your app
  • Uses optimized SSH options
  • Checks if your git remotes are up to date
  • Manages your app’s logs
  • Preloads the app in memory when managing Puma workers
  • Starts (or restarts) the Puma server after finishing a deployment
  • Opens a socket to the Puma server at a specific location in your release

You can change all options depending on your requirements. Now, Nginx needs to be configured. Create config/nginx.conf in your Rails project directory, and add the following to it (again, replacing with your parameters):

upstream puma {
  server unix:///home/<span class="highlight">deploy</span>/apps/<span class="highlight">appname</span>/shared/tmp/sockets/<span class="highlight">appname</span>-puma.sock;
server {
  listen 80 default_server deferred;
  # server_name;
  root /home/<span class="highlight">deploy</span>/apps/<span class="highlight">appname</span>/current/public;
  access_log /home/<span class="highlight">deploy</span>/apps/<span class="highlight">appname</span>/current/log/nginx.access.log;
  error_log /home/<span class="highlight">deploy</span>/apps/<span class="highlight">appname</span>/current/log/nginx.error.log info;
  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://puma;
  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;

Like the previous file, this nginx.conf contains defaults that work out-of-the-box with the configurations in your deploy.rb file. This listens for traffic on port 80 and passes on the request to your Puma socket, writes nginx logs to the ‘current’ release of your app, compresses all assets and caches them in the browser with maximum expiry, serves the HTML pages in the public folder as static files, and sets default maximum Client Body Size and Request Timeout values.

Step 7 — Deploying your Rails Application

If you are using your own Rails app, commit the changes you just made, and push them to remote from your Local Machine:

git add -A
git commit -m “Set up Puma, Nginx & Capistrano”
git push origin master

Note: If this is the first time using GitHub from this system, you might have to issue the following commands with your GitHub username and email address:

git config –global ‘Your Name’
git config –global

Again, from your local machine, make your first deployment:

cap production deploy:initial

This will push your Rails app to the Droplet, install all required gems for your app, and start the Puma web server. This may take anywhere between 5-15 minutes depending on the number of Gems your app uses. You will see debug messages as this process occurs.

If everything goes smoothly, we’re now ready to connect your Puma web server to the Nginx reverse proxy.

On the Droplet, Symlink the nginx.conf to the sites-enabled directory:

sudo rm /etc/nginx/sites-enabled/default
sudo ln -nfs “/home/deploy/apps/appname/current/config/nginx.conf” “/etc/nginx/sites-enabled/appname“

Restart the Nginx service:

sudo service nginx restart

You should now be able to point your web browser to your server IP and see your Rails app in action!

Normal Deployments

Whenever you make changes to your app and want to deploy a new release to the server, commit the changes, push to your git remote like usual, and run the deploy command:

git add -A
git commit -m “Deploy Message”
git push origin master
cap production deploy

Note: If you make changes to your config/nginx.conf file, you’ll have to reload or restart your Nginx service on the server after deploying your app:

sudo service nginx restart


Okay, so by now you would be running a Rails app on your Droplet with Puma as your Web Server as well as Nginx and Capistrano configured with basic settings. You should now take a look at other docs that can help you optimize your configurations to get the maximum out of your Rails application:

Deploy Your Rails App to AWS

Sample Rails Application

Let’s create a sample Rails application with a contact model and CRUD. The app uses Rails 4.2 and PostgreSQL:

rails new contactbook -d postgresql

After the application is generated, create a Contact model and CRUD:

cd contactbook
rails g scaffold Contact name:string address:string city:string phone:string email:string

Setup your database username and password in config/database.yml, then create and migrate the database:
[code type=ruby]rake db:create && rake db:migrate

Let’s check how its working:

rails s

Point your favorite browser to http://localhost:3000/contacts and check if everything is working properly.

Configuring Puma & Capistrano

We will now configure application for deployment. As previously mentioned, Puma is the application server and Capistrano as our deployment tool. Capistrano provides integration for Puma and RVM, so add those gems to the Gemfile. We will also use the figaro gem to save application configuration, such as the production database password and secret key:
[code type=ruby]
gem ‘figaro’
gem ‘puma’
group :development do
gem ‘capistrano’
gem ‘capistrano3-puma’
gem ‘capistrano-rails’, require: false
gem ‘capistrano-bundler’, require: false
gem ‘capistrano-rvm’

Install the gems via bundler:

bundle install

It’s time to configure Capistrano, first by generating the config file, as follows:

cap install STAGES=production

This will create configuration files for Capistrano at config/deploy.rb and config/deploy/production.rbdeploy.rb is the main configuration file and production.rb contains environment specific settings, such as server IP, username, etc.

Add the following lines into the Capfile, found in the root of the application. The Capfile includes RVM, Rails, and Puma integration tasks when finished:
[code type=ruby]
require ‘capistrano/bundler’
require ‘capistrano/rvm’
require ‘capistrano/rails/assets’ # for asset handling add
require ‘capistrano/rails/migrations’ # for running migrations
require ‘capistrano/puma’

Now, edit deploy.rb as follows:
[code type=ruby]
lock ‘3.4.0’

set :application, ‘contactbook’
set :repo_url, ‘’ # Edit this to match your repository
set :branch, :master
set :deploy_to, ‘/home/deploy/contactbook’
set :pty, true
set :linked_files, %w{config/database.yml config/application.yml}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}
set :keep_releases, 5
set :rvm_type, :user
set :rvm_ruby_version, ‘jruby-1.7.19’ # Edit this if you are using MRI Ruby

set :puma_rackup, -> { File.join(current_path, ‘’) }
set :puma_state, “#{shared_path}/tmp/pids/puma.state”
set :puma_pid, “#{shared_path}/tmp/pids/”
set :puma_bind, “unix://#{shared_path}/tmp/sockets/puma.sock” #accept array for multi-bind
set :puma_conf, “#{shared_path}/puma.rb”
set :puma_access_log, “#{shared_path}/log/puma_error.log”
set :puma_error_log, “#{shared_path}/log/puma_access.log”
set :puma_role, :app
set :puma_env, fetch(:rack_env, fetch(:rails_env, ‘production’))
set :puma_threads, [0, 8]
set :puma_workers, 0
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :puma_preload_app, false

We will edit the production.rb later, since we don’t know the server IP and other details yet.

Also, create config/application.yml to save any environment specific settings in the development environment. This file is used by the figaro gem to load the settings into environment variables. We will create the same file on the production server as well.

One thing to remember is to exclude config/database.yml and config/application.yml from the Git repository. Both files contain sensitive data which should not be checked into version control for obvious security concerns.

Creating an EC2 Instance

With the application configured and ready for deployment, it’s time to launch a new EC2 instance. Log in to the EC2 Management Console (obviously, you’ll need to sign up for an AWS account):


Click ‘Launch Instance’:


Select an Amazon Machine Image (AMI). We will be using ‘Ubuntu Server 14.04 LTS’:


Select the instance type as per your requirement. I am picking ‘t2.micro’, because it is free/cheap. For a real production server, you’d want to go bigger. Click ‘Next:Configure Instance Details’ to continue.


The default settings are good for our tutorial. Click ‘Next: Add Storage’.


The default storage is 8GB. Adjust as per your space requirement. Click ‘Next: Tag Instance’


Enter instance name. Click ‘Next: Configure Security Group’.


Click ‘Add Rule’. Select ‘HTTP’ from ‘Type’. This is required to make nginx server accessible from the Internet. Click ‘Review and Launch’


Check if all setting are correct. Click ‘Launch’


Select or create a keypair to connect to instance. You HAVE to have the private key on your local box in order to ssh into the EC2 instance. The key should live in your ~/.ssh directory. Click the checkbox ‘I acknowledge that…’ and click ‘Launch Instance’. Wait for the instance to launch.


The instance should be in the ‘running’ state. Select the instance and click ‘Connect’.


Note down the ‘Public IP’ address (It’s in the screenshot. Yours will be different.). We will need it to connect to the server.

Setup the Server

We have now provisioned the server and it’s time to setup some basic things. First of all, SSH into the server with our selected private key. Replace ‘Devdatta.pem’ with the full path to your private key:

ssh -i "Devdatta.pem" ubuntu@

You are logged into the brand new server. Update the existing packages first:

sudo apt-get update && sudo apt-get -y upgrade

Create a user named deploy for deploying the application code:

sudo useradd -d /home/deploy -m deploy

This will create the user deploy along with its home directory. The application will be deployed into this directory. Set the password for deploy user:

sudo passwd deploy

Enter password and confirm it. This password will be required by RVM for the Ruby installation. Also, add the deploy user to sudoers as well. Run sudo visudo and paste the following into the file:

deploy ALL=(ALL:ALL) ALL

Save the file and exit.

As we will be using GitHub to host our Git repository, the deploy user will need access to the repository for deployment. As such, we will generate a key pair for that user now:

su - deploy

Do not set a passphrase for the key as it will be used as deploy key.

cat .ssh/

Copy the output and set as your deploy key on GitHub.

Capistrano will connect to the server via ssh for deployment as the deploy account. Since AWS allows public key authentication only, copy the public key from your local machine to the deploy user account on the EC2 instance. The public key is your default ~/.ssh/ key, in most cases. On the server:

nano .ssh/authorized_keys

Paste your local public key into the file. Save and exit.

Git is required for automated deployments via Capistrano, so install Git on the server:

sudo apt-get install git

If you are using JRuby, install a Java Virtual Machine (JVM):

sudo apt-get install openjdk-7-jdk

Installing Nginx

First, install Nginx which is our reverse proxy:

sudo apt-get install nginx

Now, configure the default site as our requirement. Open the site config file:

sudo nano /etc/nginx/sites-available/default

Comment out the existing content and paste the following into the file.
[code type=ruby]
upstream app {
# Path to Puma SOCK file, as defined previously
server unix:/home/deploy/contactbook/shared/tmp/sockets/puma.sock fail_timeout=0;

server {
listen 80;
server_name localhost;

root /home/deploy/contactbook/public;

try_files $uri/index.html $uri @app;

location / {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Connection ”;
proxy_pass http://app;

location ~ ^/(assets|fonts|system)/|favicon.ico|robots.txt {
gzip_static on;
expires max;
add_header Cache-Control public;

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

Save the file and exit. We have configured nginx as a reverse proxy to redirect HTTP requests to the Puma application server through a UNIX socket. We will not restart nginx just yet, as the application is ready. Let’s install PostgreSQL now.

Installing PostgreSQL

sudo apt-get install postgresql postgresql-contrib libpq-dev

After postgreSQL is installted, create a production database and its user:

sudo -u postgres createuser -s contactbook

Set the user’s password from psql console:

sudo -u postgres psql

After logging into the console, change the password:
[code type=psql]postgres=# \password contactbook

Enter your new password and confirm it. Exit the console with \q. It’s time to create a database for our application:

sudo -u postgres createdb -O contactbook contactbook_production

Installing RVM & Ruby

We will use RVM to install our desired Ruby version:

su - deploy
gpg --keyserver hkp:// --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
\curl -sSL | bash -s stable

This will install RVM into the deploy user’s home directory. Logout and login again to load RVM into the deploy user’s shell. Logout with Ctrl+D and login again with su - deploy.

Now, install Ruby:

For using MRI Ruby – rvm install ruby

For JRuby – rvm install jruby

After Ruby is installed, switch to installed version:

rvm use jruby


rvm use ruby

Install bundler:

gem install bundler --no-ri --no-rdoc

Create the directories and files required by Capistrano. We will create the database.yml and application.yml files to store the database settings and other environment specific data:

mkdir contactbook
mkdir -p contactbook/shared/config
nano contactbook/shared/config/database.yml

Paste the following in database.yml:
[code type=ruby]
adapter: postgresql
encoding: unicode
database: contactbook_production
username: contactbook
password: contactbook
host: localhost
port: 5432

After that, create application.yml

nano contactbook/shared/config/application.yml

and add the following:
[code type=ruby]SECRET_KEY_BASE: “8a2ff74119cb2b8f14a85dd6e213fa24d8540fc34dcaa7ef8a35c246ae452bfa8702767d19086461ac911e1435481c22663fbd65c97f21f6a91b3fce7687ce63”

Change the secret to a new secret using the rake secret command.

Okay, we’re almost done with the server. Go back to your local machine to start deployment with Capistrano. Edit the config/deploy/production.rb to set the server IP. Open the file and paste the following into the file. Change the IP address to match with your server’s IP:
[code type=ruby]server ‘’, user: ‘deploy’, roles: %w{web app db}

Now let’s start the deployment using Capistrano:

cap production deploy

Since this is the first deployment, Capistrano will create all the necessary directories and files on the server, which may take some time. Capistrano will deploy the application, migrate the database, and start the Puma application server. Now, login to the server and restart nginx so that our new configuration will reloaded:

sudo service nginx restart

Open up the browser and point it to /contacts. The application should be working properly.


Wrap Up

Today, we learned how to deploy Rails application on AWS with Capistrano. Our application, being simple, does not use additional services such as background jobs, so I did not cover that today. But installation and configuration of such services may be required for complex applications. But that’s for another day.

Your comments and views are always welcome.