I got to spend some time playing with Docker and Rails, and really enjoyed it. Here’s a list of problems I had while at it, and how I solved them.


By the end of 2015, I got to spend a full week experimenting with Docker, as part of the Cookies Labs plan. My goals were to get to know the Docker system and philosophy, dockerize an existing project and evaluate the viability of adopting it for new projects right away.

So, I guess this is not your usual “dockerize your rails app” tutorial, there are quite a few of those around the Internet if you google around. What I intend to achieve with this post is to explain and solve some of the problems I found while following some of the aforementioned posts.

Getting the app image to build faster

Many tutorials out there for Rails apps tell you to have something like this in your dockerfile:

# ...

COPY . ./
RUN gem install bundler && bundle install
# ...

This will run bundle install from scratch everytime you build the image if any file in your app has changed, even if Gemfile or Gemfile.lock haven’t because the caché is invalidated.

However, more and more new posts about starting with Docker and Rails cover this steps, and several ways of speeding up this process have appeared:

  1. COPY the Gemfile and Gemile.lock first, RUN bundle install, then copy the rest of the app. Example
  2. Similar to (1), COPY both files to a tmp folder, RUN bundle install, then COPY the rest of the app. Example
  3. Have a bundle cache as a separate service if you work with docker-compose. A working set of files would be like:

A working Dockerfile for this technique would be:

FROM ruby:2.2.1
MAINTAINER Julio Antequera Galiano <julio@cookieshq.co.uk>

RUN apt-get update && apt-get install -y \

ENV app /app
RUN mkdir $app


COPY . ./

Our docker-compose.yml

  build: .
  command: ./script/start.sh
    - .:/app
    - "3000:3000"
    - db
    - bundle_cache

  image: postgres:9.4
    - "5432"

  image: busybox
    - /bundle_cache

And our start.sh


bundle check || bundle install

As you see, the bundle_cache service is using busybox. On the comments for the post linked above, someone mentioned the possibility of some gems having problems because their native extensions would have been compiled against a different base system (busybox) than the one our app is using (debian). During my tests I didn’t find any problems with this, but it might be something to take into consideration.

Any of these three solutions will speed up your build time generously. The third one seems more elegant, if you want to take the cool EaaS (Everything-as-a-Service) approach.

Getting your Capybara Specs to pass

My acceptance tests were failing, and I recurred to this Thoughtbot’s post to try and fix them. However, the configuration they provided for Headless in the rails_helper didn’t make it for me. Most probably it was caused by a clash with my project’s current specs config, starting from. In any case, here’s how I got it working, given that you already have the xvfb package in your apt-get line and the headless gem installed:

RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods

  config.before(:suite) do
    @headless = Headless.new

  config.after(:suite) do

Consider moving to puma and foreman

As of Rails 5, puma is the default server for Rails apps, but if you’re dockerizing a Rails 4 (or even older!) app, you’ll find that good old Webrick is really slow behind Docker. Switching to puma and foreman really helps overcome this.

Whitelist your IP for the web console

To avoid warnings about blocked ip’s in your server log and get the Rails web console working again, In your config/environments/development.rb, add the following line:

config.web_console.whitelisted_ips = '' # your docker machine IP

Keep your html emails working

If you are sending emails with embedded styles and images, you might find they’re not working properly from inside docker. On your mailer configuration, you need to change the ‘localhost’ by your IP:

config.action_mailer.raise_delivery_errors = true # not needed but helpful
config.action_mailer.default_url_options = { host: '', port: 3000 }
config.action_mailer.asset_host = ""

Guard won’t work 🙁

I couldn’t get Guard to work out of the box. After researching a bit, I found a couple of interesting posts:

The first link mentions docker-osx-dev a script that enforces file sync between the host and the container. It was designed to work with boot2docker, and it’s support for docker-machine is experimental yet, so tread with care. More info here.


I hope this post is helpful. After a week, I think Docker is really great and will be very helpful for us in the near future. However, we prefer to wait to have things like Guard working again, so we can completely translate the way we work into Docker, and not get crazy about getting it done. In any case, we are going to keep an eye on it!

Picture ‘Breaching Humpback Whale (Megaptera novaeangliae)’ by Gregory “Slobirdr” Smith, used under CC BY-SA 2.0 license.