Building Rails Applications in Docker

Docker images can provide a powerful interface for deploying your Rails applications. Everything your application needs in order to run can be packaged inside a Docker image. The Docker images can then be deployed from your laptop, staging and to production. This greatly increases the parity between your development and production environments when making a Rails application.

However, always needing to create a Docker image just to test your code can be time consuming if you’re not careful. Consider the following Ruby application:

config.ru:

app =  proc do |env|
  [200, {}, %w(hello world)]
end
run app

Gemfile:

source 'https://rubygems.org'

gem 'rack'
gem 'nokogiri'

Dockerfile:

FROM ruby:2.2.2

ADD . /app
WORKDIR /app
RUN bundle install

EXPOSE 9292
CMD rackup -E none

Everytime you make an update to your application in the config.ru file, Docker would have to install and recompile the nokogiri gem all the time to create your Docker image.

Since you don’t need to update the Gemfile compared to the rest of your application, you can move the installation of your Ruby dependencies (bundle install) to a separate layer in your Docker image. To do this, just update your Dockerfile as follows:

FROM ruby:2.2.2

ADD Gemfile /app/Gemfile
WORKDIR /app
RUN bundle install
ADD . /app

EXPOSE 9292
CMD rackup -E none

Now, everytime you update config.ru, Docker will use the layer generated by 'RUN bundle install'.

Doing this approach will optimize the time it takes to build your Docker images. There are other ways to optimize Rails and other web applications in Docker-based deployments. I talked some of it in my presentation in this year’s RailsConf. The presentation can be viewed here: https://youtu.be/lpHgNC5bCbo. Chapter 2 of my book, Docker High Performance also describes these optimizations in detail.