[RUBY] Easy deployment with Capistrano + AWS (EC2) + Rails


I used Capistrano to deploy my own Rails app.

Basically, I referred to the following article. (Capistrano edition) The most polite AWS commentary in the world. Until you bring your Rails app to AWS using EC2

In this article, I've summarized the parts that were filled with errors along with the reference article, so I hope you find it helpful.

File to create

Local side

Server side (EC2)

Source code

Gemfile List of gems to install.


group :development, :test do
 gem 'capistrano'
 gem 'capistrano-bundler'
 gem 'capistrano-rails'
 gem 'capistrano-rbenv'

group :production, :staging do
  gem 'unicorn'

Other literature may use the gem'capistrano3-unicorn', but it is not used here for detailed settings.


This is the configuration file for the entire capistrano.


require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
#Set to read the file that describes the task. Specify the location and extension.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

In the reference article, the last line is "~ / *. Rb", but in the current version, rake is the default, so use that.

Note that the file format of lib / capistrano / tasks / unicorn.rake that you will create later is **. Rake **. Let's make the set file format the same file format. (I got a little stuck here.)


This file describes the settings of the production environment.


#Describe the IP of the EC2 server, the user name to log in to the EC2 server, and the role of the server.
server '**.***.***.***', user: 'yuki', roles: %w[app db web]

#Describe the key information to ssh login to the server to deploy
set :ssh_options, keys: '~/.ssh/app_key_rsa'


Describe the settings that apply to both the production environment and the stading environment.


#Fixed version of capistrano
lock '3.14.1'

#Application name to deploy
set :application, 'golfour'

#Git repository to clone
set :repo_url, '[email protected]:app/app_aws.git'

#The branch to deploy. The default does not have to be master.
set :branch, 'master'

#The directory to deploy to.
set :deploy_to, '/var/www/rails/app'

# secret_base_Added to read the key
set :linked_files, %w[config/master.key]

#A file with a symbolic link.
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/settings.yml', '.env')

#A folder with symbolic links.
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

#The number of versions to keep. Save up to 5 history.
set :keep_releases, 5

#version of ruby
set :rbenv_ruby, '2.5.1'

#The level of the log to output.
set :log_level, :debug

namespace :deploy do
  desc 'Restart application'
  task :restart do
    invoke 'unicorn:restart'

  desc 'Create database'
  task :db_create do
    on roles(:db) do |_host|
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:create'

  desc 'Run seed'
  task :seed do
    on roles(:app) do
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:seed'

  after :publishing, :restart

  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do

The following two codes are added in addition to the reference article.

Here, for symbolic links (files that you don't want to open, like setting gitignore)

# secret_base_Added to read the key
set :linked_files, %w[config/master.key]

#A file with a symbolic link.
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/settings.yml', '.env')


A file that describes the unicorn setup task.


#Specify the unicorn pid file and configuration file directory
namespace :unicorn do
  task :environment do
    set :unicorn_pid,    "#{current_path}/tmp/pids/unicorn.pid"
    set :unicorn_config, "#{current_path}/config/unicorn/production.rb"

  #Method to start unicorn
  def start_unicorn
    within current_path do
      execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"

  #Method to stop unicorn
  def stop_unicorn
    execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"

  #Method to restart unicorn
  def reload_unicorn
    execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})"

  #Method to kill unicron
  def force_stop_unicorn
    execute :kill, "$(< #{fetch(:unicorn_pid)})"

  #task to start unicorn
  desc 'Start unicorn server'
  task start: :environment do
    on roles(:app) do

  #task to stop unicorn
  desc 'Stop unicorn server gracefully'
  task stop: :environment do
    on roles(:app) do

  #If unicorn is already running, restart it, if not, start it task
  desc 'Restart unicorn server gracefully'
  task restart: :environment do
    on roles(:app) do
      if test("[ -f #{fetch(:unicorn_pid)} ]")

  #task to kill unicorn
  desc 'Stop unicorn server immediately'
  task force_stop: :environment do
    on roles(:app) do

As I explained in Capfile, please note that it is unicorn.rake. If it is set in .rb in Capfile, it will be unicorn.rb.

config/unicorn/production.rb This is the unicorn configuration file.


#Number of workers. See below
$worker = 2
#Decide how many seconds have passed before removing the worker
$timeout = 30
#Note that your application name, current, is added.
$app_dir = '/var/www//rails/golfour/current'
#Specify the port number to receive the request. See below
$listen = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
#PID management file directory
$pid = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
#Directory of files that spit out error logs
$std_log = File.expand_path 'log/unicorn.log', $app_dir

#Defined so that what is set above is applied
worker_processes  $worker
working_directory $app_dir
stderr_path $std_log
stdout_path $std_log
timeout $timeout
listen  $listen
pid $pid

#Set whether to hot deploy
preload_app true

#Define what to do before fork. See below
before_fork do |server, _worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
      Process.kill 'QUIT', File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH

#Define what to do after fork. See below
after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection



  secret_key_base: jr934ugr89vwredvu9iqfj394vj9edfjcvnxii90wefjc9weiodjsc9o i09fiodjvcijdsjcwejdsciojdsxcjdkkdsv 
(#Paste the generated random number)



default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

  <<: *default
  database: golfour_development
  username: yuki
  password: password
  host: db
  socket: /tmp/mysql.sock

  <<: *default
  database: golfour_test
  username: yuki
  password: password
  host: db-test
  socket: /tmp/mysql.sock

  <<: *default
  database: <%= ENV['DB_NAME'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOSTNAME'] %>


Describe the environment variables used in datacbase.yml here.





#Directory settings for various logs
  error_log  /var/www/rails/mumu/current/log/nginx.error.log;
  access_log /var/www/rails/mumu/current/log/nginx.access.log;
#Maximum capacity to receive processing
  client_max_body_size 2G;
  upstream app_server {
#Unicorn socket path to work with
    server unix:/var/www/rails/mumu/current/tmp/sockets/.unicorn.sock fail_timeout=0;
  server {
    listen 80;
    server_name; #Change it to your own IP address!
#Waiting time for the next request (seconds)
    keepalive_timeout 5;
#Directory to read static files
    root /var/www/rails/mumu/current/public;
#Cache directory
    try_files $uri/index.html $uri.html $uri @app;
    location @app {
      # HTTP headers
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;
#Where to place the error page
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/mumu/current/public;

Deploy command

bundle exec cap production deploy

Error list

Insufficient memory

Occurs after deploying. It was caused by insufficient memory of EC2.


virtual memory exhausted

Solution: Create a swap file

This is a method of amplifying the memory so that it can be used by temporarily moving the unused memory to another location. If you refer to the following article, you can easily implement it, so please do.

reference: What is a swap file [Rails] Deploy to EC2 with Capistrano: Troubleshoot EC2 Out of Virtual Memory

Missing secret_key_base for 'production' environment

This is also an error that occurred when executing the deployment.

The cause was that the master.key file was missing. The secret_key_base can be found in the credentials.yml.enc file in the first place. However, this file was encrypted and I needed master.key to read it, but I was getting an error because I didn't have it.


・ Create shared / master.key -Added master.key to symbolic links

By doing so, the error disappeared.

Reference: Automatically deploying with Capistrano gives "Missing secret_key_base for'production'environment" error

Recommended Posts

Easy deployment with Capistrano + AWS (EC2) + Rails
I tried automatic deployment with CircleCI + Capistrano + AWS (EC2) + Rails
For beginners! Automatic deployment with Rails6 + CircleCI + Capistrano + AWS (EC2)
[Rails] Nginx, Puma environment deployment & server study [AWS EC2]
[Docker] ECS automatic deployment --Docker edition-
[Terraform] ECS automatic deployment --Terraform edition-
I tried automatic deployment with CircleCI + Capistrano + AWS (EC2) + Rails
For beginners! Automatic deployment with Rails6 + CircleCI + Capistrano + AWS (EC2)
Deploy to EC2 with CircleCi + Capistrano
Launch Rails on EC2 (manual deployment)
rails AWS deployment is not reflected
[EC2 / Vue / Rails] EC2 deployment procedure for Vue + Rails
[Rails] AWS EC2 instance environment construction
[Rails] AWS deployment error encounter summary
Deployed using Docker + Rails + AWS (EC2 + RDS)
Using Java with AWS Lambda-Implementation-Stop / Launch EC2
[Rails] Image posting by CarrierWave [AWS EC2]
[AWS] Publish rails app with nginx + puma
Deploy Rails to ECS Fargate with AWS Copilot
Recipe for deploying Rails apps on AWS EC2
Create an EC site with Rails5 ⑤ ~ Customer model ~
Create an EC site with Rails 5 ⑩ ~ Create an order function ~
EC2 ✕ Manual deployment with Docker / docker-compose / Asset compilation
Easy to display hello world with Rails + Docker
Pass User credential to deployment destination with Jenkins + Capistrano
[Heroku] Associate AWS S3 with the deployed Rails app
Create an EC site with Rails 5 ⑨ ~ Create a cart function ~
Create an EC site with Rails5 ④ ~ Header and footer ~
Create an EC site with Rails5 ⑥ ~ seed data input ~
Launch Rails on EC2
Rails deploy with Docker
[Rails 6] RuntimeError with $ rails s
Rails Heroku deployment procedure
Handle devise with Rails
[Rails] Learning with Rails tutorial
Deploy RAILS on EC2
[Rails] Heroku deployment flow
[Rails] Test with RSpec
[Rails] Development with MySQL
Supports multilingualization with Rails!
Double polymorphic with Rails
Created the first LINEbot on AWS Cloud9 (Rails + Heroku deployment)
[AWS] What I'm glad I did with my first deployment
Gcc error solution with "pip install annoy" on AWS EC2
What to do if an SSH key authentication error occurs during automatic deployment to EC2 with Capistrano