Dockerize a Rails App

Heard lots about Docker and you want to try it out? This tutorial will demonstrate one way to get a simple Rails app running in Docker.

You're going to need a linux environment to follow along. If you're using a Mac I recommend you just follow the directions I wrote about getting set up with Vagrant. That will cover installing Docker and Docker Compose. If you already have a linux environment, you can just install them as follows.

Installing Docker and Docker Compose

$ sudo apt-get update && apt-get install wget
$ wget -qO- | sh
$ sudo usermod -aG docker vagrant
$ sudo docker run hello-world

The last command should show you a bunch of things including: Hello from Docker.

  • Docker Compose - This will help us manage multi-container apps. Installation docs are here or just copy and paste:
$ sudo -i
$ curl -L`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
$ exit

Dockerizing your app

Clone your app down first. I'm using a demo rails app called storedom from the good people at Turing.

$ git clone ~/src/storedom

The problem with that app is that it is using SQLite. We're going to use PostgreSQL so we can have two Docker containers running and talking to each other. To do that, we'll need to change a few files: database.yml, and the Gemfile

development: &default  
  adapter: postgresql
  encoding: unicode
  database: storedom_dev
  pool: 5
  username: postgres
  host: postgres

  <<: *default
  database: storedom_test

  <<: *default
  database: storedom
source ''

gem 'rails', '4.1.4'  
gem 'pg'  
gem 'sass-rails', '~> 4.0.3'  
gem 'uglifier', '>= 1.3.0'  
gem 'coffee-rails', '~> 4.0.0'  
gem 'jquery-rails'  
gem 'turbolinks'  
gem 'jbuilder', '~> 2.0'  
gem 'faker'  
gem 'haml-rails'  
gem 'therubyracer'  
gem 'less-rails-bootstrap'

group :development do  
  gem 'spring'

group :development, :test do  
  gem 'capybara'
  gem 'pry', :require => 'pry'

The Dockerfile is a set of instructions Docker uses to build your container. You start with a base image and then layer on various commands that will install software, organize directories, etc. The commands are carried out in sequence.

Here is an example that will work for storedom:

FROM ruby:2.2.0

WORKDIR /usr/src/app/

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev

RUN curl -o /usr/local/bin/gosu -SL "$(dpkg --print-architecture)" && chmod +x /usr/local/bin/gosu

COPY Gemfile /usr/src/app/Gemfile  
COPY Gemfile.lock /usr/src/app/Gemfile.lock

RUN bundle install

COPY . /usr/src/app  
# copy our project into the WORKDIR

CMD ["rails", "s"]  
# this is the default command we will run when starting the container*

Docker has good documentation on building a Dockerfile, but you can see a quick explanation of the commands we are using below:


Our base image is ruby 2.2.0. This is a basic Docker image with Ruby 2.2.0 installed on top of Ubuntu. It is maintained by Docker. The rest of our commands will build off this starting point.


The WORKDIR is the working directory for all the other commands.


The first RUN installs two libraries that we will need. The second installs gosu, a tool we use to avoid permissions hell.


COPY copies files into the container. We are copying in the Gemfile and Gemfile.lock so that we can install our gems in the container. After they are copied into the container, we RUN bundle install to install all our gems. Then we copy our project into the container.


The default command for when the container is started. There are a few acceptable form for CMD. This is the preferred form.

Docker Compose

Docker Compose, formerly called fig, is a tool that helps manage building, running, and linking Docker containers. It consists of a YAML file that sits in your project directory, called docker-compose.yml, and a CLI.

Below is a docker-compose.yml that we can use to get for this project.

  build: .
  command: bundle exec rails s -p 3000 -b ''
    - .:/storedom
    - "3000:3000"
    - postgres:postgres

  image: postgres
    - "5432"

So what do those things mean? Luckily, the file is reasonably self-explanitory and well documented, but we can go into a little detail about what each key does.

Container Name

This sample app needs two containers running: rails and postgres. The first will run the rails app and the second will run the postgres database. They are defined at the top level of docker-compose.yml, and we can use these friendly names to refer to the containers in the Docker Compose CLI.


build maps to a path to a directory that contains a Dockerfile. We have a Dockerfile in the same directory as the docker-compose.yml, and therefor just use..


command overrides the default command as defined in the Dockerfile [ ] check the actual command used in the Dockerfile. Do we even need to use command?


volumes mounts a path on the host to a path in the new container. Here, we are mapping the current directory to the /storedom directory in the container.


Similar to our Vagrant setup, ports maps ports on the host to ports in the container. We're keeping everything on 3000 just to make it all easy. Using a string here isn't mandatory, but is idiomatic.

This is one of the really cool parts of Docker Compose. links links containers together; in this case, we are giving our app container, rails, access to our database container postgres.


Every container in Docker Compose needs either build or image. image will pull down the mapped image.

Tying it all together

Now, you should have everything you need to run your app in Docker containers. Run $ docker-compose up and Docker Compose will build your containers and run them. Learn more about the Docker Compose CLI in the docs.

Go out and cargo-cult to your heart's content. Get it? Because Docker? And cargo!