Capistrano
On this page
- Change Database User Password
- Define the Environment Variable
- Database Configuration
- Capistrano Related Gems
- Capify Project
- Configure Capfile
- Configure Deploy
- Define Custom Tasks
- Establish SSH Connection
- Configure Allowed Hosts
- Run Deploy Command
- Create Inventory File
- Run Puma Playbook
- Map IP to domain
- Run SSL Playbook
- Verify Running App
Change Database User Password
To change the database password, run the ansible playbook provided in Change Database Password.
Define the Environment Variable
export CAPT_DATABASE_PASSWORD='new-password'
echo $CAPT_DATABASE_PASSWORD
The new-password
is your new password used in the previous step.
Database Configuration
The config/database.yml file will pickup the password from this environment variable:
production:
<<: *default
database: capt_production
username: deploy
password: <%= ENV["CAPT_DATABASE_PASSWORD"] %>
The database user is deploy. The Packer built the image with the deploy database user has create database permission. When Capistrano runs as deploy user, it will be able to run rails db:create
. Do not change the username for production. You can change the default password for the deploy database user as explained above.
Capistrano Related Gems
Add Capistrano related gems to the development group in the Gemfile:
group :development do
gem 'capistrano', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-bundler', require: false
gem 'ed25519', '>= 1.2', '< 2.0'
gem 'bcrypt_pbkdf', '>= 1.0', '< 2.0'
end
I am not using the capistrano3-puma gem because it is not compatible with Puma 6.4.2. The gem has issues due to breaking changes related to daemonizing the Puma process. Instead, we will be using a Puma playbook for running and restarting Puma.
The config/deploy.rb in the demo Rails project has Puma restart capistrano task that will automatically restart on deploy.
Capify Project
Run bundle install
. You can now capify your Rails project by running:
cap install
Configure Capfile
The Capfile contents:
# Load DSL and set up stages
require "capistrano/setup"
# Include default deployment tasks
require "capistrano/deploy"
# Load the SCM plugin appropriate to your project:
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Include tasks from other gems included in your Gemfile
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
Configure Deploy
Add these lines to config/deploy.rb:
# config valid for current version and patch releases of Capistrano
lock "~> 3.18.1"
server '54.188.245.219', user: 'deploy', roles: %w{app db web}, port: 2222
set :application, "capt"
set :branch, 'main'
set :repo_url, "git@github.com:bparanj/capt.git"
set :deploy_to, '/home/deploy/apps/capt'
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/master.key')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'public/uploads')
set :keep_releases, 5
set :default_env, {
'CAPT_DATABASE_PASSWORD' => ENV['CAPT_DATABASE_PASSWORD']
}
- Replace 54.188.245.219 with your Rails server static IP address.
- Change
capt
for application to your application name. - Change the repo_url to your project repo url.
- Replace
your-app-name
in deploy_to to your Rails app name.
Do not change /home/deploy/apps/
. The /home/deploy/apps
directory is used because the prebuilt Packer image has already created it with the proper permissions for the deploy user. This allows Capistrano to create all the necessary directories under your Rails app name folder. Otherwise Capistrano deploy will fail due to directory permission issues.
Define Custom Tasks
Copy the following to the end of config/deploy.rb:
namespace :deploy do
namespace :check do
before :linked_files, :upload_config_files do
on roles(:app), in: :sequence, wait: 10 do
# Upload master.key
unless test("[ -f #{shared_path}/config/master.key ]")
upload! 'config/master.key', "#{shared_path}/config/master.key"
end
# Upload database.yml
unless test("[ -f #{shared_path}/config/database.yml ]")
upload! 'config/database.yml', "#{shared_path}/config/database.yml"
end
end
end
end
end
namespace :deploy do
desc 'Create database if it does not exist'
task :create_db do
on roles(:db) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rails, 'db:create'
end
end
end
end
before 'deploy:migrate', 'deploy:create_db'
end
# Capistrano task to print environment variables
namespace :deploy do
desc 'Print environment variables'
task :print_env do
on roles(:app), in: :sequence, wait: 5 do
execute :printenv
end
end
after 'deploy:published', 'deploy:print_env'
end
The first time deploy creates the production database. We also print the environment variables to verify that they are set correctly on the server by Capistrano. Otherwise, you will run into database connection issues due to missing database password. We also upload the master.key and database.yml that will be retained across deployments. Capistrano will automatically run migrations when code is deployed.
Establish SSH Connection
Copy ~/.ssh/id_ed25519.pub key file on your development machine to the Rails server ~/.ssh/authorized_keys.
cat ~/.ssh/id_ed25519.pub
Copy the output and save it on the server:
vi ~/.ssh/authorized_keys
Now you can test the SSH connection on the development machine by running:
ssh -p 2222 -i ~/.ssh/id_ed25519 deploy@54.188.245.219
Replace the IP address with your EC2 static IP address. By default the Packer image uses the port 2222 for SSH connection. If this works, the key is setup correctly and Capistrano will be able to deploy your Rails app.
Configure Allowed Hosts
In config/environments/production.rb, add:
config.hosts << "clickplan.net"
config.hosts << "www.clickplan.net" # If you also want to explicitly allow requests to www subdomain
Replace clickplan.net
with your domain name.
Run Deploy Command
On the development machine, run:
cap production deploy
Create Inventory File
In the hivegrid.dev project, create inventory.ini
inside ansible folder:
[ec2_instances]
54.188.245.219
[ec2_instances:vars]
ansible_port=2222
ansible_user=ubuntu
ansible_ssh_private_key_file=/Users/bparanj/work/hivegrid.dev/javascript/rails-server.pem
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
- Replace the IP address with the static IP of your Rails EC2 instance.
- Replace the path to pem file that was downloaded using keyDownload.js.
Run Puma Playbook
Run puma playbook in the ansible/playbooks/deploy directory. Replace the capt
with the name of your app in the command line. In the ansible directory, run:
ansible-playbook -i inventory.ini ./playbooks/deploy/puma.yml -e "app_name=capt"
Map IP to domain
This is covered in Mapping IP to Domain
Run SSL Playbook
Run ssl playbook in the ansible/playbooks/deploy directory.
ansible-playbook -i inventory.ini ./playbooks/deploy/ssl.yml
Verify Running App
Browse to the health check endpoint: https://clickplan.net/up
. It should display a green background.
The restart-puma playbook in the ansible/playbooks/deploy directory is handy if you make any changes to Puma config.