ttrhow
Last Updated: May 17, 2017
·
27.65K
· sheharyar
Explosionsinthesky websterhall   new york  2007

Deploying Rails app using Nginx, Puma and Capistrano 3

This guide is an alternative to James Dullaghan's Deploying Rails app using Nginx, Unicorn, Postgres and Capistrano to Digital Ocean that I made for myself. This is the first time I'm working with Puma, so if I've missed something, or if there's something that can be done in a better way, please comment and let me know.

Start by creating a Droplet (Ubuntu 14.04 LTS x64) and ssh to root in terminal:

ssh root@server              # replace 'server' with your vps ip

Change your password:

passwd

Create a new user and set privileges

adduser username
visudo

Find user privileges section, and duplicate the root line changing it to your new user:

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

Press (CTRL+O), then (CTRL+X) to save and quit. Now it's time to configure ssh:

nano /etc/ssh/sshd_config

For better security, it's recommended that you disable root and change the ssh port (anything between 1025..65536):

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

At the end of sshd_config, enter:

UseDNS no
AllowUsers username

Press (CTRL+O), then (CTRL+X) to save and quit. Reload ssh:

reload ssh

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

ssh username@server -p 7171

If everything's working fine, you can close root. We need to install packages now; in your new user ssh session, enter:

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

Install rvm, ruby and rubygems:

curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm requirements
rvm install 2.1.0
rvm use 2.1.0 --default
rvm rubygems current

(RVM installation might fail, and it may ask you to download its GPG Keys, so do that and retry)

Install rails and bundler:

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

Shake hands with Github/Bitbucket and Generate a public/private key pair:

ssh -T git@github.com
ssh -T git@bitbucket.org
ssh-keygen -t rsa 

Add it as a deployment key for your repository (Instructions: Github & Bitbucket). Also try cloning on the server manually to make sure it's working fine.

Add your own ssh key to the VPS' authorized_keys. In your local terminal session, enter:

cat ~/.ssh/id_rsa.pub | ssh -p 7171 username@server 'cat >> ~/.ssh/authorized_keys'

Now, in your project's Gemfile, add these and bundle:

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
end

Run:

cap install

A Capfile in your root and a deploy.rb file in config folder will be created for you. Replace the contents of your Capfile with:

# 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 }

Replace the contents of config/deploy.rb with this (modify parameters according to your app):

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

set :repo_url,        'git@example.com:user/app.git'
set :application,     'appname'
set :user,            'username'
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/puma.pid"
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/id_rsa.pub) }
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, false  # Change to true if 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"
    end
  end

  before :start, :make_dirs
end

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."
        exit
      end
    end
  end

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

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

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

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

Create config/nginx.conf in your project directory and add this to it (again replacing with your parameters):

upstream puma {
  server unix:///home/username/apps/appname/shared/tmp/sockets/appname-puma.sock;
}

server {
  listen 80 default_server deferred;
  # server_name example.com;

  root /home/username/apps/appname/current/public;
  access_log /home/username/apps/appname/current/log/nginx.access.log;
  error_log /home/username/apps/appname/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;
}

Commit changes, push and make the initial deploy:

git add -A
git commit -m "Set up Puma, Nginx & Capistrano"
git push origin master
cap production deploy:initial

If everything goes smoothly, back in your ssh session, symlink your nginx.conf and start nginx. You only need to do this once (but you might have to restart nginx whenever you update your conf file).

sudo rm /etc/nginx/sites-enabled/default
sudo ln -nfs /home/username/apps/appname/current/config/nginx.conf /etc/nginx/sites-enabled/appname
sudo service nginx start

Now onwards, whenever you want to push a new deploy:

git add -A
git commit -m "Deploy Message"
git push
cap production deploy
Say Thanks
Respond

13 Responses
Add your response

17165
None

This looks great, I'm going to try it on a new VPS instance. I followed Digital Ocean's guide, but never got Foreman working. Your guide doesn't use it, and I think this would be easier to host more than one site on a single VPS. I'm loving Puma.

over 1 year ago ·
17166
None

production log is blank and getting something wen wrong. How to debug?

over 1 year ago ·
17169
Default profile 4 normal

how do you handle server reboots.?
right now i have to go in each rails project and do 'cap production puma:start'. great tutorial btw :)

==EDITED==

found solution. puma comes with upstart configs (ubuntu)
https://github.com/puma/puma/tree/master/tools/jungle/upstart

i needed to add this to your script in the deploy-task

desc 'Set config/puma.rb-symlink for upstart'
task :pumaconfigln do
on roles(:app) do
execute "ln -s #{sharedpath}/puma.rb #{fetch(:deployto)}/current/config/puma.rb"
end
end

after :finishing, :pumaconfigln

over 1 year ago ·
17257
Explosionsinthesky websterhall   new york  2007

@jbmyid Yeah, has happened to me once. I was able to fix it using this:
http://stackoverflow.com/a/24794116/1533054

You might want to change the unicorn commands with the puma ones (cap production puma:restart).

over 1 year ago ·
17598
None

For rvm, the installation failed for me. What did work was:

curl -sSL https://get.rvm.io | bash -s stable --rails

Thoughts?

over 1 year ago ·
17727
97cd5c77c41bed6428f03810dacf0046

I have one problem, because I follow step to step this tutorial... but, when I run "cap production deploy:initial" show me the next error:

Net::SSH::AuthenticationFailed: Authentication failed for user sbrocos@servername

(and I restinstal netssh with 2.7.0 version)

Thanks

over 1 year ago ·
19329
Explosionsinthesky websterhall   new york  2007

@benmorganio Guide updated, thanks.

over 1 year ago ·
19336
E703a41d6ed925b6f5cf44e64d3cdfd1

I followed this setup but Im getting this error: 403 Forbidden
I have already check /home/user permission (it's ok: 755).

over 1 year ago ·
19854
Explosionsinthesky websterhall   new york  2007

@snysantos Can you explain a bit more about your set up? I can't help you with only this information.

I wrote this guide for myself, and I use it to set-up every production rails app on a VPS, and the last time I used this guide was 4 days ago, so believe me when I say, it works.

over 1 year ago ·
21402
None

All the stuff works, but at the point of the deploy, puma and capistrano doesnt start. (Doesnt do the "rails server" command) but if I do the rails s on the server it works. Problem is.. doesnt do rails s automatically. Can u help ?

over 1 year ago ·
22556
Explosionsinthesky websterhall   new york  2007

@Vor7exx Must be something different in your configuration because Puma is configured to automatically start after the first deploy and each other deploy simply restarts your puma instance.

You can run this command (on your local machine) to start puma on the server:

cap production puma:start
over 1 year ago ·
23376
Terno circle

My deploy is equal this tutorial but i have problem with environment variable, check:
http://stackoverflow.com/questions/33117318/missing-secret-token-and-secret-key-base-for-production-environment

over 1 year ago ·
28321
Meeeeeee me

Thanks :) will try this one.

7 months ago ·