Ruby on Rails Deploy Explained: Twelve Factor

Sinopsis

This is the second article in our series on Ruby on Rails deployment. As outlined in our previous post, there are two main approaches to deploying a Ruby on Rails application: one where the server has a lot of knowledge about the application, and one where it has minimal knowledge. The former approach is more traditional, and often involves tools like Capistrano and Ansible.

The latter approach is more suited to platform-as-a-service hosting solutions like Heroku, which pioneered this approach for Ruby on Rails. Heroku developed a set of guidelines called 12 factor, which is a standard for organizing a web application. The 12 factor approach is designed to increase portability and reduce differences between development and production environments.

To use Heroku, Dokku or any similar service, your application has to conform to 12 factor principles. In general, this brings in high portability to your application and reduces the knowledge needed to launch it.

So, let's take some simple steps to prepare your application to twelve factor.

Config

12 factor suggests to keep config in environment variables. You can use dotenv or figaro for this. Econfig can be a good solution as it allows to store settings in the database (there is an option to edit them from admin panel) with a fallback to environment variables. But anyway, you will have to pass the database connection to the application as a setting. This is the setting that you always have.

Fortunately, Rails already does a good job about this, and since 4.1 it handles this perfectly. If an application has defined an environment variable DATABASE_URL, its settings override the ones in config/database.yml. So you shouldn't do anything about it.

For all other settings inside the application, you should read them as if they came from environment variables and only dotenv, foreman or figaro should know where they actually came from.

Database

Don't forget to replace Sqlite with Postgres, in case you haven't done it yet. Sqlite is great but it's about file system and cannot be attached as a background service.

Processes, disposability and port binding

12 factor recommends to run your application as a plain Unix process. For Ruby on Rails, this basically means that you should take care of the application server. Such process should export everything via simple port/socket binding instead of relying on application server or serving files directly from the file system.

To do it, we will use a standard Procfile convention. Procfile is a way to declare which processes should be run by the application. The simplest Procfile for your application is:

web: ./bin/rails server thin -p $PORT -e ${RACK_ENV:-development}

If you have delayed job or sidekiq:

web: ./bin/rails server thin -p $PORT -e ${RACK_ENV:-development}
worker: ./bin/rake jobs:work
# or
worker: ./bin/sidekiq # assuming you prepared a binstub which is recommended

To export everything via port without relying on static serving assets, I suggest using rails_12factor gem. Just put it in Gemfile in production.

By default, Rails does not serve static assets in production. Instead, it assumes that they are served by using something like tryfiles Nginx directive. But we treat our application as a simple process that binds port and exposes all it has via that port. That's why, we need Rails to serve static files.

This also reduces dev-prod divergence, so that your application in production works closer to how it works on your development machine.

Logs

12 factor offers to treat logs as streams, not files. This means that you should direct logs to stdout, (almost) like it works in development. It is already done if you used rails_12factor gem.

You're done!

Now you are ready to install Dokku and use it. We'll explain it in the next article.