Today we will show you how to do this with Docker and what are the first steps to “dockerize” a Ruby application.

What is Docker

Docker is a tool that allows developers to specify which is the operating system configuration that will run the application. Docker provides an abstraction layer for configurations of this virtual environment called the container. What makes docker to be adopted on a large scale is that it allows you to package all the dependencies of a stack into a single container unit.

Docker installation

See the official installation manuals for your operating system.

But basically you can run on Linux:

sudo apt-get install docker

Or on OSX:brew install docker

brew install docker

Multiple environments with Docker

In real life working with projects, you need to organize images to work with various environments. For example, in a production image, you do not need to have your text editor set up.

In an image that just runs your test suite, you do not need to have the same tunning configuration of the database.


The images are for you to have a bootstrap of your application. So if you want to upload an image with Ubuntu you can create a Dockerfile with the following content:

FROM ubuntu
CMD echo "I am container running Ubuntu"

Wow! With just one FROM <system-operating-image>, you can already start a machine with Ubuntu.

To dock an app, that is, port to the world of containers, we need to define which operating system and which tools we install. There are some official images that already guarantee the basic set of tools for each stack. So it’s worth extending these images and adapting to your need. In this example below, we’ll use the official image that contains the ruby.

FROM ruby
RUN ruby -e "p ENV"

The images can also be versioned, and each version gets split with <image:version>, so it could be FROM ruby:2.2.4to specifically download the 2.2.4 version.

In Dockerfile you have specified the image you want to use and a command you want to execute within the image.

This way, we now need to download the image and build locally with docker build.

Docker build

To build the image above, you must use the docker building, the folder where the Dockerfile is.

When running locally, you will have an output like this:

docker build.
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ruby
 ---> cc0ac907dc6d
Step 2 : RUN ruby -e "p ENV"
 ---> Running in e0dc789ac0b0
{"RUBY_MAJOR"=>"2.3", "BUNDLER_VERSION"=>"1.11.2", "HOSTNAME"=>"e5c68db50333", "RUBYGEMS_VERSION"=>"2.6.2", "HOME"=>"/root", "BUNDLE_APP_CONFIG"=>"/usr/local/bundle", "BUNDLE_BIN"=>"/usr/local/bundle/bin", "RUBY_VERSION"=>"2.3.0", "PATH"=>"/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "BUNDLE_PATH"=>"/usr/local/bundle", "GEM_HOME"=>"/usr/local/bundle", "RUBY_DOWNLOAD_SHA256"=>"ba5ba60e5f1aa21b4ef8e9bf35b9ddb57286cb546aac4b5a28c71f459467e507", "PWD"=>"/", "BUNDLE_SILENCE_ROOT_WARNING"=>"1"}
 ---> 2e5fdfd56049
Removing intermediate container e0dc789ac0b0
Successfully built 2e5fdfd56049

Now if you try to run again, you will see that the image in the output has already been cached.

➜  ruby docker build .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ruby
 ---> 2e5fdfd56049
Step 2 : RUN ruby -e "p ENV"
 ---> Using cache
 ---> 549f215a2569
Successfully built 549f215a2569

Note that it has not rotated the Ruby command because this part is already cached.

But what happened in practice? Let’s inspect the images.

Docker images

Use the command docker images to check which images are available on your computer:

$ docker images
ruby         latest  549f215a2569   5 minutes ago   725.4 MB

725 MB? To make a simple hello worldCry

The official ruby image is based on UBUNTU and so it’s pretty big.

There is a version based on Alpine Linux , which is a more compact version of linux.

So let’s change the Dockerfile to use the alpine.

FROM ruby:alpine
RUN ruby -e "p ENV"

And we will update the image again by running one $ ruby docker build..

And now by checking the size of the image:

$ ruby docker images
REPOSITORY    TAG      IMAGE ID       CREATED              SIZE
ruby          latest   a3446813b648   About a minute ago   125.3 MB

Much better with 125.3 MB.

Cool, now we have a space to work with ruby inside a container. Just synchronize the project into the container. And there are some ways to do this:

Copying resources

Doing the build, copying the application files to the image is useful when you want to do the self-contained deploy of the application. Changes to these files will need to be committed to the container, which makes development more difficult, but to deploy in production environments is the best way to work.

Through volumes

Mapping volumes into containers are useful when you want to change the container’s files without having to rebuild the image. Ideal for development environments, where you change a file and have the need to test the changes in time.

Linking Features

Linking features are useful for making containers more compact. Ideal for organizing and keeping containers simple but sharing information with each other.

Docker run

The docker command buildgenerated an image of a container with Ruby installed.

This allows you to reuse this image. Extend the image by adding more functionality or configuring it to the project specifications.

You can also run a command directly from within the container.

docker run -it ruby:alpine bash -c "ruby -e '3.times {|i|puts i}'"

Nice! I’m running ruby without having to install anything locally!

Build Options

In the build process, it is possible to create a repository/ tag in the image with the option -t <repo/tag>. This can be useful for re-taking pictures in the future. Example:

docker build -t digitalresults/sinatra.

Checking the image with docker images:

REPOSITORY                   TAG     IMAGE ID       CREATED              SIZE
digitalresults/sinatra  latest  1f305fc79a88   About a minute ago   141.2 MB

The image now appears with REPOSITORY and will make it easier to reuse in other containers.

To run the server with the image type:

docker run -p 4567:4567 digitalresults/sinatra ruby app.rb
[2017-02-28 15:13:12] INFO  WEBrick 1.3.1
[2017-02-28 15:13:12] INFO  ruby 2.3.1 (2017-02-28) [x86_64-linux]
== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup
from WEBrick
[2017-02-28 15:13:12] INFO  WEBrick::HTTPServer#start: pid=1 port=4567

Where it -pserves to bridge the gap between <door of container>:<local door>.

Trying to access locally via localhost: 4567 will not be available because the bind was made to while the docker waits for the host

That way we will have to change the server sinatra to bind in the correct host:

require "bundler/setup"
require "sinatra"

set :bind, ''

$counter = 0
get "/counter" do
  puts "counter: #{$counter += 1}"


The docker registry is the official platform provided by the docker staff itself for you to keep the docker images and share images.

Docker push

With the docker push command, you can share your updated image in an image repository or in the Docker Hub. Afterward, it is not necessary to run docker build recompiling everything. So just make one docker pull to download the finished image.

Docker pull

The docker pull command is used to download the ready-made images from the repository.

Each image is made up of multiple layers and you can download and synchronize only the fragments of the image.

Docker compose

You should now be thinking of installing your entire toolbox into a container and not messing up your machine by spreading random libraries wherever you go.

But slow! Take a look at the docker compose that he serves exactly for this! It helps you compose container units and work with a subset of containers that talk to each other.

Already in the process of using in production, it is also worth seeing:

Kubernetes and docker swarm for orchestration of containers

Apache mesos and [open shift] for PAAS

A little more!

And you’re using docker in your day to day life? Be sure to share your experience in the comments!