Ruby on Rails: Capistrano for easy deployment and configuration to Ubuntu with Nginx and Puma
26 Sep 2018Capistrano is a tool written in ruby to automate the deployment of multiple versions of an application. In this post I’ll demonstrate how to use Capistrano to deploy and configure a Rails app to an Ubuntu Server with Nginx and Puma.
Table of Contents
Setup the Ubuntu Server
Login to the server as root user.
ssh root@example.emmanuelcorrales.com
Create a super user called deploy.
useradd -m deploy -G sudo
Login as deploy.
su - deploy
Update Ubuntu then install Curl, Git and Nginx.
sudo apt-get update
sudo apt-get install curl git-core nginx -y
Setup Ruby on Rails environment
Install RVM.
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
source /home/deploy/.rvm/scripts/rvm
rvm requirements
Install Ruby via RVM.
rvm install 2.2.2
rvm use 2.2.2 --default
ruby --version
Install Rails and Bundler without docs to save space and nobody needs docs on the server.
deploy@server:~:$ gem install rails -v '5.2.0' -V --no-ri --no-rdoc
deploy@server:~:$ gem install bundler -V --no-ri --no-rdoc
Setup SSH
Create .ssh directory
deploy@server:~:$ mkdir -m 700 .ssh
deploy@server:~:$ touch ~/.ssh/authorized_keys
deploy@server:~:$ chmod 600 ~/.ssh/authorized_keys
Setup SSH access to the server. Create key pairs from your local machine.
ssh-keygen -t rsa
This will generate two files ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub. Copy the public key ~/.ssh/id_rsa it to the server.
ssh-copy-id deploy@example.emmanuelcorrales.com
The server will also use the same public key to pull changes to the Rails from the repository. Add the newly created public key (~/.ssh/id_rsa.pub) to your repository’s deployment keys. If you are using Github you can find the instructions here.
Setup Capistrano
“Capify” the Rails app
Add this gem to your Gemfile.
group :development do
gem 'capistrano', '~>3.10', require: false
end
Then install it.
bundle install
From your apps local root directory generate the necessary configuration files.
bundle exec cap install
This command will generate four files Capfile, config/deploy.rb config/deploy/staging.rb and config/deploy/production.rb.
We want to deploy the Rails app called example.emmanuelcorrales.com whose repository is hosted at git@github.com:EmmanuelCorrales/example.git. Different verisons of the app would be stored at the /home/default/example.emmanuelcorrales.com directory. Configure Capistrano to install it there by changing the contents of config/deploy.rb to look like the code below.
# config valid for current version and patch releases of Capistrano
lock "~> 3.11.0"
set :application, 'example.emmanuelcorrales.com'
set :repo_url, 'git@github.com:EmmanuelCorrales/example.git'
# Deploy configurations
set :deploy_via, :remote_cache
set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
Add Ruby on Rails deployment tasks
Add this gem to your Gemfile under the capistrano gem.
gem 'capistrano-rails', '~>1.4', require: false
Then install it.
bundle install
Edit the Capfile to look like this:
# Load DSL and set up stages
require "capistrano/setup"
# Include default deployment tasks
require "capistrano/deploy"
# Load the git SCM plugin appropriate to your project:
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
require "capistrano/rails"
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
Symlink Rails shared files and directories like log, tmp and public/uploads. Enable it by setting the linked_dirs and linked_files options at the deploy.rb.
## Linked Files & Directories (Default None):
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', '.bundle', 'public/system', 'public/uploads'
append :linked_files, 'config/database.yml', 'config/secrets.yml', 'config/application.yml'
Configure Puma
Add this gem to your Gemfile under the capistrano gem.
gem 'capistrano3-puma', require: false
Then install it.
bundle install
Add these lines to your Capfile.
require 'capistrano/puma'
install_plugin Capistrano::Puma
Add these lines to the config/deploy.rb.
# Puma configurations
set :puma_threads, [4, 16]
set :puma_workers, 0
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 :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
Configure Nginx
Add this line to your Capfile below the line install_plugin Capistrano::Puma.
install_plugin Capistrano::Puma
Add these lines to the config/deploy.rb. These are the parameters for the Nginx configurations that will be uploaded to the server.
# Nginx configurations
set :nginx_config_name, "#{fetch(:application)}_#{fetch(:stage)}"
set :nginx_flags, 'fail_timeout=0'
set :nginx_http_flags, fetch(:nginx_flags)
set :nginx_server_name, "localhost #{fetch(:application)}.local"
set :nginx_sites_available_path, '/etc/nginx/sites-available'
set :nginx_sites_enabled_path, '/etc/nginx/sites-enabled'
set :nginx_socket_flags, fetch(:nginx_flags)
set :nginx_ssl_certificate, "/etc/ssl/certs/#{fetch(:nginx_config_name)}.crt"
set :nginx_ssl_certificate_key, "/etc/ssl/private/#{fetch(:nginx_config_name)}.key"
set :nginx_use_ssl, false
Upload the nginx configuration to the server.
bundle exec cap production puma_nginx:config
Deploying the Rails app with Capistrano
Edit the contents of config/deploy/production.rb to look like this:
server "example.emmanuelcorrales.com", user: "deploy", roles: %w{web app db}
Deploy to the server.
bundle exec cap production deploy