Rails 4 API deployment example to Amazon EC2 using Capistrano 3, Nginx, Puma, GitHub, and RVM? Rails 4 API deployment example to Amazon EC2 using Capistrano 3, Nginx, Puma, GitHub, and RVM? nginx nginx

Rails 4 API deployment example to Amazon EC2 using Capistrano 3, Nginx, Puma, GitHub, and RVM?


I will augment my answer if requested, but since this is pretty simple I'll just make you a list.Basically, with Capistrano 3 and Puma 2.6 here's enough to get you going:

  1. Add puma to Gemfile in production group.
  2. Require 'capistrano/puma' from Capfile.
  3. Setup any puma options in config/deploy.rb if needed.
  4. Run cap puma:config to copy puma config file to the server.
  5. Run cap <stage> deploy.
  6. Setup nginx's upstream to the socket path specified in the server's Puma config.
  7. Restart nginx.


This question is pretty old but I have the exact same setup: Capistrano 3, Puma and nginx so I figure it may still help someone, especially since Puma is now the default for Rails 5. My project is not open source so I can't point to a Github public repository, but here is my complete Capistrano configuration for anyone to take a look. It has been working flawlessly for months now.

To properly start Puma in the production server upon EC2 reboot / new instance creation, take a look at my answer here:

Puma restart fails on reboot using EC2 + Rails + Nginx + Capistrano

#config/puma.rb

workers Integer(ENV['WEB_CONCURRENCY'] || 2)threads_count = Integer(ENV['MAX_THREADS'] || 5)threads threads_count, threads_countpreload_app!rackup      DefaultRackupport        ENV['PORT']     || 3000environment ENV['RACK_ENV'] || 'development'on_worker_boot do  # Worker specific setup for Rails 4.1+  # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot  ActiveRecord::Base.establish_connectionend

#config/deploy.rb

# config valid only for current version of Capistranolock '3.4.0'set :application, 'deseov12'set :repo_url, 'git@bitbucket.org:augustosamame/deseov12.git'set :user, 'deploy'set :branch, :masterset :deploy_to, '/home/deploy/deseov12'set :rails_env, 'production'set :pty, trueset :linked_files, %w{config/database.yml config/application.yml}set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}set :keep_releases, 3set :rvm_type, :userset :rvm_ruby_version, 'ruby-2.2.4' # Edit this if you are using MRI Rubyset :delayed_job_command, "bin/delayed_job"set :puma_rackup, -> { File.join(current_path, 'config.ru') }set :puma_state, "#{shared_path}/tmp/pids/puma.state"set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"set :puma_bind, "unix://#{shared_path}/tmp/sockets/puma.sock"    #accept array for multi-bindset :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, :appset :puma_env, fetch(:rack_env, fetch(:rails_env, 'production'))set :puma_threads, [0, 8]set :puma_workers, 0set :puma_worker_timeout, nilset :puma_init_active_record, trueset :puma_preload_app, false#load 'lib/capistrano/tasks/seed.rb'namespace :db do  desc 'Resets DB without create/drop'  task :nuke do    on primary :db do      within release_path do        with rails_env: fetch(:stage) do          execute :rake, 'db:reset db:migrate db:seed'        end      end    end  endend# Default branch is :master# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp# Default deploy_to directory is /var/www/my_app_name# set :deploy_to, '/var/www/my_app_name'# 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('config/database.yml', 'config/secrets.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')# Default value for default_env is {}# set :default_env, { path: "/opt/ruby/bin:$PATH" }# Default value for keep_releases is 5# set :keep_releases, 5namespace :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_dirsendnamespace :deploy do  after :restart, :clear_cache do    on roles(:web), in: :groups, limit: 3, wait: 10 do      # Here we can do anything such as:      # within release_path do      #   execute :rake, 'cache:clear'      # end    end  endend

#Capfile

# Load DSL and set up stagesrequire 'capistrano/setup'# Include default deployment tasksrequire 'capistrano/deploy'# Include tasks from other gems included in your Gemfile## For documentation on these, see for example:##   https://github.com/capistrano/rvm#   https://github.com/capistrano/rbenv#   https://github.com/capistrano/chruby#   https://github.com/capistrano/bundler#   https://github.com/capistrano/rails#   https://github.com/capistrano/passenger#require 'capistrano/rvm'require 'capistrano/bundler'require 'capistrano/rails/assets'require 'capistrano/rails/migrations'require 'capistrano/puma'require 'capistrano/delayed-job'# require 'capistrano/passenger'# require 'capistrano/rbenv'# require 'capistrano/chruby'# Load custom tasks from `lib/capistrano/tasks` if you have any definedDir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

#config/deploy/production.rb

# server-based syntax# ======================# Defines a single server with a list of roles and multiple properties.# You can define all roles on a single server, or split them:# server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value# server 'db.example.com', user: 'deploy', roles: %w{db}# your actual server ip address should go here:server 'xx.xx.xx.xxx', user: 'deploy', roles: %w{web app db} #Deploy# server 'xx.xx.xx.xxx', user: 'deploy', roles: %w{web app db} #Prod

#lib/capistrano/tasks

namespace :deploy do  desc 'Runs rake db:seed for SeedMigrations data'  task :seed => [:set_rails_env] do    on primary fetch(:migration_role) do      within release_path do        with rails_env: fetch(:rails_env) do          execute :rake, "db:seed"        end      end    end  end  after 'deploy:migrate', 'deploy:seed'  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, :pumaconfiglnend

Nginx Server configuration:

#etc/nginx/sites-enabled/default

# You may add here your# server {#   ...# }# statements for each of your virtual hosts to this file### You should look at the following URL's in order to grasp a solid understanding# of Nginx configuration files in order to fully unleash the power of Nginx.# http://wiki.nginx.org/Pitfalls# http://wiki.nginx.org/QuickStart# http://wiki.nginx.org/Configuration## Generally, you will want to move this file somewhere, and start with a clean# file but keep this around for reference. Or just disable in sites-enabled.## Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.##upstream app {  # Path to Puma SOCK file, as defined previously  server unix:/home/deploy/deseov12/shared/tmp/sockets/puma.sock fail_timeout=0;}server {  listen 80;  server_name localhost;  root /home/deploy/deseov12/current/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;}server {  listen 443 default ssl;  server_name lodeseo.com;  root /home/deploy/deseov12/current/public;  ssl on;  ssl_certificate /etc/nginx/ssl/www_lodeseo_com_bundle.crt;  ssl_certificate_key /etc/nginx/ssl/www_lodeseo_com.key;  ssl_session_timeout  5m;  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';  #ssl_ciphers  HIGH:!aNULL:!MD5;  ssl_prefer_server_ciphers   on;  ssl_session_cache shared:SSL:10m;  ssl_dhparam /etc/ssl/certs/dhparam.pem;  location ~ ^/(assets|fonts|system)/|favicon.ico|robots.txt {    gzip_static on;    expires max;    add_header Cache-Control public;  }  try_files $uri/index.html $uri @app;  location @app {    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    proxy_set_header Host $host;    proxy_set_header X-Forwarded-Proto https;    #proxy_set_header X-Forwarded-Proto $scheme;    proxy_redirect off;    proxy_pass http://app;  }}    # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests    #location /RequestDenied {    #   proxy_pass http://127.0.0.1:8080;    #}    #error_page 404 /404.html;    # redirect server error pages to the static page /50x.html    #    #error_page 500 502 503 504 /50x.html;    #location = /50x.html {    #   root /usr/share/nginx/html;    #}    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000    #    #location ~ \.php$ {    #   fastcgi_split_path_info ^(.+\.php)(/.+)$;    #   # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini    #    #   # With php5-cgi alone:    #   fastcgi_pass 127.0.0.1:9000;    #   # With php5-fpm:    #   fastcgi_pass unix:/var/run/php5-fpm.sock;    #   fastcgi_index index.php;    #   include fastcgi_params;    #}    # deny access to .htaccess files, if Apache's document root    # concurs with nginx's one    #    #location ~ /\.ht {    #   deny all;    #}# another virtual host using mix of IP-, name-, and port-based configuration##server {#   listen 8000;#   listen somename:8080;#   server_name somename alias another.alias;#   root html;#   index index.html index.htm;##   location / {#       try_files $uri $uri/ =404;#   }#}# HTTPS server##server {#   listen 443;#   server_name localhost;##   root html;#   index index.html index.htm;##   ssl on;#   ssl_certificate cert.pem;#   ssl_certificate_key cert.key;##   ssl_session_timeout 5m;##   ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;#   ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";#   ssl_prefer_server_ciphers on;##   location / {#       try_files $uri $uri/ =404;#   }#}