Deploying Rails 5.2 App using Capistrano 3

Time : 20 minutes

In this article, we will deploy a Rails 5.2 app using Capistrano 3. I am using Nginx as the web server, Passenger as the application server and RVM to manage Ruby versions.

Capistrano 3 Setup

Add the capistrano gems to Gemfile.

group :development do
  gem "capistrano", "~> 3.10", require: false
  gem "capistrano-rails", "~> 1.4", require: false
  gem 'capistrano-passenger', '~> 0.2.0'
  gem 'capistrano-rvm'

  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
end

I am using RVM to manage Ruby versions and Passenger with Nginx. Run bundle install. Capify your Rails 5.2 project.

cap install STAGES=production

Add to Capfile:

require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
require 'capistrano/bundler'
require 'capistrano/rails'
require 'capistrano/passenger'
require 'capistrano/rvm'

set :rvm_type, :user
set :rvm_ruby_version, '2.6.1'

The order in which you add the require statement is critical, otherwise you will run into problems during deploy. Edit deploy.rb:

set :application, "your-app-name"
set :repo_url, "git@example.com:me/your-repo.git"

set :deploy_to, '/home/deploy/your-app-name'

append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', '.bundle', 'public/system', 'public/uploads'
append :linked_files, 'config/database.yml'
append :linked_files, %w{config/master.key}

set :migration_role, :app

Troubleshooting Tips

If you get the error:

SSHKit::Runner::ExecuteError: Exception while executing as deploy@some-ip: no implicit conversion of Array into String
SSHKit::Runner::ExecuteError: Exception while executing as * no implicit conversion of Array into String

during deploy, the cause is this line:

append :linked_files, %w{config/master.key}

Change the array to string like this:

append :linked_files, 'config/master.key'

Note that we have master.key that is Rails 5.2 specific. The migration role is set on app server so we don't need to deploy code on db server for running migrations. For now, everything in one one server. Eventually, when the site grows, we can deploy the database server on a different machine. Edit production.rb:

server 'your-server-ip', user: 'deploy', roles: %w{app db web}

Create all the linked files on the server such as database.yml, master.key, secrets.yml etc. Deploy the Rails 5.2 app:

cap production deploy

Login to your domain registrar. For instance, in Namecheap, go to Advanced DNS, create an A record. Remove the parking record and make sure there is only one @ and one www records. Both @ and www must point to your server IP. After 30 minutes, you will see your Rails app served on your domain.

Note: If you are using sqlite3 in production. It will disappear when you deploy. Because the releases folder will have a new folder and it will not have the production.sqlite file. To persist the sqlite3 database across deploys, add it to the linked_files in deploy.rb:

append :linked_files, 'config/master.key', 'db/production.sqlite3'

You will now be able to use the same sqlite3 database across deploys. If you get:

Gem::LoadError: can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile.

In the ActiveRecord source, you can see:

gem "sqlite3", "~> 1.3.6"

This is the reason we are not able to use version 1.4 of sqlite3.


Related Articles