Build and Deploy a Rails VPS, Part 2

“Capify” a new Rails app and deploy it to your VPS using Capistrano and Puma.

This is the second half of a two-part tutorial. Missed the first part? Visit Part 1: Setting up a VPS with rbenv and Ruby 2.3.0.

Now that the VPS is set up, how about we use it to run an honest-to-goodness Rails app?

Capistrano is a great tool for deploying Rails, and it will work perfectly well with an Ubuntu 14.04 VPS running rbenv. For this example we’ll use the Puma app server, since it is simple to set up and has exceptionally good Capistrano integration.

We’ll also use sqlite3 for the database. Normally you wouldn’t use sqlite3 in production, but it’s the simplest option and helps keep this tutorial short and to the point.

Capistrano works from your local computer, using SSH to remotely control your VPS. Run all these steps on your local development machine unless otherwise specified.

1 Create a new Rails app

rails new blog -d sqlite3

This creates a new Rails app called “blog” using sqlite3 as the database. Feel free to name the app whatever you want, or use an existing app.

In order to deploy the app, you’ll also need to push it to a git repository, like Bitbucket or GitHub. This step is outside the scope of this tutorial. I’ll assume you are familiar with git.

2 Install your SSH private key on the VPS

ssh-copy-id deployer@<your VPS IP address>

Capistrano relies on your private key to work its SSH magic. Make sure your private key is installed in the deployer user on your VPS. You need to be able to SSH to the VPS without being prompted for a password.

Mac users: get the ssh-copy-id command with brew install ssh-copy-id.

3 Add Capistrano and Puma gems

gem "capistrano-rails", :group => :development
gem "capistrano3-puma"

Add these gems to your Gemfile, and then:

bundle install

4 Capify

bundle exec cap install STAGES=production

This Capistrano command prepares your Rails project for deployment by generating all the necessary Capistrano files:

5 Load the Rails and Puma plugins for Capistrano

At the bottom of Capfile, uncomment/add the following lines:

require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/puma"

This tells Capistrano that we’d like to load the “recipes” for deploying a Rails app using bundler and Puma. That means we don’t have to write any code of our own! With a few config settings it will work out of the box.

You do not need the capistrano/rbenv plugin.

6 Configure the deploy recipes

Set up config/deploy.rb (you can delete the rest of the sample deploy.rb that Capistrano generated):

set :application, "blog"
set :repo_url, "<your git@ bitbucket/github URL goes here>"
set :linked_dirs, %w(
  bin log vendor/bundle public/system
  tmp/pids tmp/cache tmp/sockets
set :puma_bind, "tcp://"

Capistrano will deploy your app by fetching your app directly from the master branch of the git repository (e.g. Github or Bitbucket). The repo_url must be correct or Capistrano will fail early on in the deployment process. Make sure your latest code is pushed to origin/master!

Puma requires some tmp directories to be in place; the linked_dirs setting ensures these are set up.

Finally, puma_bind tells Puma to publish our app on TCP port 8080. Normally Puma connects to Nginx on a UNIX domain socket, but I’m skipping Nginx to keep this tutorial short. Instead, Puma will serve web requests directly on port 8080.

7 Tell Capistrano about your VPS

In config/deploy/production.rb (again, you can delete the rest of the generated sample config):

server "<your VPS IP address goes here>",
       :user => "deployer",
       :roles => %w(web app db)

Here’s where we tell Capistrano how to SSH into the VPS, and what portions of the app are hosted there. Since we only have one server, it necessarily is responsible for all roles: %w(web app db).

8 Specify the Rails secret key

In production, Rails expects a SECRET_KEY_BASE environment variable to be present. This key is used to encrypt cookies and keep your application safe from attackers.

We’ll make use of another rbenv plugin to set this environment variable. As the deployer user on your VPS, run:

echo "SECRET_KEY_BASE=34d69b7015b267fbd5efd91241c9ad1d7d08bd55f2b157558098426e0f4bb2e163a6899f90dc35ca58d5821ec16dac55bed151939eca80463111872499c86ae7" > ~/.rbenv/vars

The rbenv-vars plugin automatically loads the contents of the .rbenv/vars file into the environment whenever any Ruby process is executed. This includes Rails, so SECRET_KEY_BASE will get passed via the environment to our app. Easy!

(In practice you should’t use my secret key; generate your own by running rails secret.)

9 Create the deployment directory

As root on the VPS:

sudo mkdir -p /var/www/blog
sudo chown deployer /var/www/blog

Capistrano will deploy your app to /var/www/<application> by default, where the application name is defined by set :application in deploy.rb. Deployment will fail if this directory doesn’t exist or is not writable by the deployer user.

10 Copy the Puma config to the VPS

Back on your local machine, run:

bundle exec cap production deploy:check
bundle exec cap production puma:config

The deploy:check command builds out the necessary directory structure. Then puma:config generates a puma.rb configuration file and places it in the correct location on the VPS. We’re in the final stretch!

11 Deploy the app!

bundle exec cap production deploy

It may take a few minutes to complete the first deploy, due to the bundle install step, which needs to download all the necessary gems and compile native extensions.

But if all goes well, the app will soon be up an running on port 8080 of your VPS IP address. And don’t panic: a brand new Rails app with no customization will show this message:

The page you were looking for doesn’t exist.

That’s because you have a blank app with no routes, controllers, or views. Generate some, commit and push those to git, then run the deploy command again to see the results.


Let’s review

Deploying a Rails app to your own VPS is not trivial, but with tools like Capistrano and tutorials like this one, I hope you agree that it is not rocket science.

Indeed, once you fully automate your provisioning and project setup process, as I have done with my capistrano-mb and rails-template projects, deploying new apps can be a breeze.

If you have a knack for Linux system administration, or just a desire to own and control the complete deployment stack and avoid vendor lock-in, I encourage you to give it a shot.