Using Chef and Capistrano to deploy a Rails application on Ubuntu 16.04

Cloud platforms vs dedicated server

Every developer is looking for a right way to deploy applications to production. Often, the question of the deployment is delayed at a later time, and it’s well known that many applications do not launch in production and developers just use solutions like Heroku for staging or demo servers. Cloud platforms are easy to use, but they are expensive and not so configurable, unlike your own VPS.

Server configurations management

The most popular tools for this which are frequently used in the Ruby world are Chef and Chef Solo.

With Chef you can define the steps required to configure a server to fulfill a “role”, for example a Rails application server or a database server and then apply combinations of these roles to a particular remote machine.

Chef often works centrally. Central server “knows” the role that should be applied to a big number of other servers. If you are upgrading the role, the changes are applied to all of these servers automatically.

But if you don’t need to configure a big number of servers it’s easiser to use our local environment to determine the configuration and server roles, and then manually apply the configuration to the servers. To work with that we need knife-solo.

Adds a handful of commands to chef’s knife tool that aim to make working with chef-solo as powerful as chef-server.


My typical stack:

  • Ubuntu 16.04
  • Nginx — the best web server of all time
  • Monit — utility for managing and monitoring Unix systems
  • PostgreSQL — relational database
  • Redis — is a popular in-memory key-value store
  • RVM — ruby versions management tool

Let’s create a folder which contains our Chef:

Next, we have to set up some useful gems with bundler:

Then set up gems and initialize Chef:

Generated directory structure:

  • cookbooks — directory for Chef cookbooks. This directory will be used for vendor cookbooks and should be added to .gitignore
  • data_bags — directory for Chef Data Bags
  • nodes — directory for Chef nodes (folder with bunches of roles for each machine)
  • roles — directory for Chef roles (groups of used cookbooks and additional attributes)
  • site-cookbooks — directory for your custom cookbooks

Install external cookbooks with berkshelf for typical rails stack

Make a base role:

Which will contain a run list and some additional attributes.

apt-packages — will be our custom cookbook with some needed libraries:

Also we need to write two cookbooks to configure monit and nginx which you can copy from my chef-template repo.

Prepare server

For example, I’m using the most popular cloud server hosting DigitalOcean. Let’s create a droplet with ubuntu 16.04.

The next step is creating node with IP of your machine.

Also update nginx config with your domain or IP, add user and ssh public key.

Install Chef on a given host.

Finally, we’re able to upload the current kitchen to the target host and run chef-solo on that host. In a few minutes our config will be applied.


Web interface with states of monitored processes will be available by /monit route.


Capistrano is a Ruby framework for writing tasks related to deploying any application, in our case a Rails app, to a remote server. This include tasks like release management, checking out the code from a Git repository onto the remote server and uploading configuration files into our app each time when we need it.

As an example, I’ll use my rails-api-template which contains:

  • puma — server for Ruby web applications
  • dotenv — shim to load environment variables from .env into ENV
  • devise_token_auth — token based authentication solution for rails
  • rack-cors — support for Cross-Origin Resource Sharing for Rack compatible web applications

Move to your project folder and add Capistrano with some useful gems to the development group into Gemfile.

Install gems and generate Capistrano configs:

Capistrano gems should be required to Capfile.

We can point the machine’s IP, folder to deploy and a Git branch in config/deploy/production.rb

Puma config:

Note: app_path should be same as in nginx config

File with deploy tasks— config/deploy.rb

We need to create .env.production file to add ENV variables

Initialize folder on a server and upload configs by following two commands:

Finally run deploy command.

Ready to use templates

By the way, Chef and Capistrano configurations from this article are available in public repositories on GitHub.

Last revision: 30.09.2017

Software Engineer. Interested in Full-Stack Development and DevOps.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store