How to deploy your rails app to Amazon EC2 using Capistrano, nginx and puma.

Shreya mundra
7 min readMay 10, 2021

step 1 — create a simple rails application

step 2 — push that app on github.

step 3 — create your Amazon EC2 Instance.

Go to Amazon AWS Console

https://console.aws.amazon.com/

Sign in, or sign up.

Once you’re logged in and have an account ready to go, click Services.

In the dropdown menu, select EC2 under the Compute menu.

On this new page, click Launch Instance.

Choose the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type.

Click the Select button.

Since this is a starter project, I’m going to choose the t2.micro instance, eligible for the free tier.

Click Next: Configure Instance Details — nothing to change here, unless you want to.

Click Next: Add Storage — I’m going to keep the defaults here, as well, since this is really just a proof of concept kind of thing.

Click Next: Add Tags — again, nothing to go here.

Click Next: Configure Security Group

  1. Select the Create a new security group radio button
  2. There’s already an SSH open on port 22 to 0.0.0.0/0
  3. Next, add a new rule. Here’s what I created:

Security rule settings:

  • Type: Custom TCP
  • Protocol: TCP
  • Port Range: 80
  • Source: Custom: 0.0.0.0/0

Keeping port 80 open to the world is desired behaviour — you want anyone to be able to access your site once it’s live.

Click Review and Launch

Confirm everything looks the way you want, and finally click Launch.

Amazon will ask you to select an existing key pair or create a new key pair. I’m assuming you either haven’t done this before (and therefore have no key pair), or, like me, are trying to set up a fully sandboxed Rails App. So let’s select Create a new key pair.

and name key-value pair.

click download key-value pair. save the file. and click launch the instance.

step 4 — now find the downloaded key-value pair and navigate to that folder.

i have put that key in server-keys folder so

Then you’ll want to connect to your instance using its Public DNS.

The command looks like ssh -i "filterrific-key-pair.pem" ubuntu@some-dns.computer.amazon.aws.

If you need a more specific command for your keypair name and DNS address, click the Connect button in the Amazon Resource Groups panel.

after that command you will be in a shell prompt which looks like:-

step 5 — set up Capistrano in your rails project:-

gem ‘capistrano’

gem ‘capistrano-rails’

gem ‘capistrano-bundler’

gem ‘capistrano-rbenv’

gem ‘capistrano3-puma’

and then do bundle install.

If everything worked, go ahead and commit this change.

Now generate the Capistrano config files by running-

cap install STAGES=production

This creates three files: Capfile, config/deploy.rb and config/deploy/production.rb.

and in Capfile uncomment the following lines:-

require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/rbenv"
require 'capistrano/puma'
install_plugin Capistrano::Puma //you have to add this

now setup deploy.rb file to look like this:-

# config valid for current version and patch releases of Capistranolock "~> 3.16.0"set :application, "demo_app"set :repo_url, 'https://github.com/shreya-bacancy/demo_app.git'set :deploy_to, '/home/ubuntu/demo_app'set :use_sudo, trueset :branch, 'master'set :linked_files, %w{config/master.key config/database.yml}set :rails_env, 'production'set :keep_releases, 2set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
set :linked_files, %w{config/database.yml config/master.key}
# Default branch is :master# ask :branch, `git rev-parse --abbrev-ref HEAD`.chompset :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.access.log"set :puma_error_log, "#{release_path}/log/puma.error.log"set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa.pub) }set :puma_preload_app, trueset :puma_worker_timeout, nilset :puma_init_active_record, true # Change to false when not using ActiveRecordnamespace :puma dodesc 'Create Directories for Puma Pids and Socket'task :make_dirs doon roles(:app) doexecute "mkdir #{shared_path}/tmp/sockets -p"execute "mkdir #{shared_path}/tmp/pids -p"endendbefore :start, :make_dirsend

now we will configure production.rb

server '65.2.73.207', user: 'ubuntu', roles: %w{web app db}
set :ssh_options, {
forward_agent: true,
auth_methods: %w[publickey],
keys: %w[/home/shreya/server-keys/filterrific-key-pair.pem]
}

step 6 — prepare server to run Capistrano tasks-

firstly run your server:-

shreya@shreya-Vostro-3500:~ $ ssh -i ~/server-keys/filterrific-key-pair.pem ubuntu@ec2-65-2-73-207.ap-south-1.compute.amazonaws.com

Create the directory that the app will live in: (make sure that you are in server console)

ubuntu@ip-172-31-4-157:~$ sudo mkdir -p /var/www

By default, the ubuntu user doesn’t have access to this directory, so change the ownership:

ubuntu@ip-172-31-4-157:~$ sudo chown ubuntu /var/www

update all existing pacakge :-

ubuntu@ip-172-31-4-157:~$ sudo apt-get update && sudo apt-get -y upgrade

Then install all the dependencies required to install Ruby:

ubuntu@ip-172-31-4-157:~$ sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev libsqlite3-dev

now install rbenv. Clone the rbenv repo:

ubuntu@ip-172-31-4-157:~$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv

then, add ~/.rbenv/bin to your $PATH so you can use its CLI.

ubuntu@ip-172-31-4-157:~$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc

Add this command to your bash profile so you can load rbenv automatically.

# ~
ubuntu@ip-172-31-4-157:~$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc

Restart bash to get those changes to apply.

# ~
ubuntu@ip-172-31-4-157:~$ source ~/.bashrc

Then we'll install the ruby-build plugin to add rbenv install which simplifies the install process for new Ruby versions (which we will surely need for Rails 6)

# ~
ubuntu@ip-172-31-4-157:~$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

Now you'll be able to see all the available ruby versions with this command:

# ~
ubuntu@ip-172-31-4-157:~$ rbenv install -l

For Rails 6, we need Ruby 2.5.0 or newer. At the time of writing, Rails 6 sets it at 2.5.1

# ~
ubuntu@ip-172-31-4-157:~$ rbenv install 2.5.1

The installation may take some time. This is a good spot to make some coffee.

Up next, set your global default ruby version using

# ~
ubuntu@ip-172-31-4-157:~$ rbenv global 2.5.1

You can check with

# ~
ubuntu@ip-172-31-4-157:~$ ruby -v

Now we need to configure our ability to work with gems. First, let's turn off local documentation generation by adding a setting to our ~/.gemrc file

# ~
ubuntu@ip-172-31-4-157:~$ echo "gem: --no-document" > ~/.gemrc

With our gem process configured, let's install bundler. This step gave me issues before, and I realized I installed the latest bundler, but Rails 6 generated using 1.17.1. Make sure to specify your version - the version you download on the server should match what's written in your Gemfile.

# ~
ubuntu@ip-172-31-4-157:~$ gem install bundler -v 1.17.1

Now update all pacakage and install NodeJs pacakage

ubuntu@ip-172-31-4-157:~$ sudo apt-get install nodejs

now install npm

ubuntu@ip-172-31-4-157:~$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
ubuntu@ip-172-31-4-157:~ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
ubuntu@ip-172-31-4-157:~$ sudo apt-get update
ubuntu@ip-172-31-4-157:~$ sudo apt-get install yarn

step 7–: Set up your EC2 server with your git credentials

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

it will prompt for a filename where you store your key, press enter to save it in default location.

also it will ask for passphrase but it is optional.

now go to ~/.ssh folder and run nano id_rsa.pub

Copy the content of above pub file, go to GitHub, click on Settings, then click SSH and GPG keys. Click New SSH key or Add SSH Key.

step 8 — Setup Nginx

now we install nginx on the server

ubuntu@ip-172-31-4-157:~$ sudo apt-get install nginx

Next, let’s configure the default server block:

# ~
ubuntu@ip-172-31-4-157:~$ sudo nano /etc/nginx/sites-available/default

And restart Nginx server -

ubuntu@ip-172-31-4-157:~$ sudo service nginx restart

Add the following in your file:-

upstream app {
# Path to Puma SOCK file, as defined previously
server unix:/home/ubuntu/demo_app/shared/tmp/sockets/demo_app-puma.sock;
}
server {
listen 80;# If you're planning on using SSL (which you should), you can also go ahead and fill out the following server_name variable:
server_name localhost;
# Don't forget to update these, too
root /home/ubuntu/demo_app/public;
try_files $uri/index.html $uri @app;location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

now save and exit.

step 9 — Commit your local changes to git and deploy to server

shreya@shreya-Vostro-3500:~/ROR/demo_app (master)$ git add .
shreya@shreya-Vostro-3500:~/ROR/demo_app (master)$ git commit -m "update with capistrano configuration"
shreya@shreya-Vostro-3500:~/ROR/demo_app (master)$ git push
shreya@shreya-Vostro-3500:~/ROR/demo_app (master)$ cap production deploy

step 10 — Go to browser and type your server public IP Address.

if everything works well!! then you see your rails app!!

--

--