How to dockerize a Rails application

Viral Patel
5 min readNov 19, 2021

Microservices architecture is a particular way of designing software applications so that it can be deployed independently, test independently, and scaled independently. Read What are microservices? to get a more deep understanding of this particular architecture pattern.

One of the first few steps you as a developer or ops person can do to move towards microservices architecture is to start containerizing your applications.

If Rails is part of your stack, this blog will explain to you how to dockerize your Rails application. This blog is not a docker tutorial, yet you will be instructed with some best practices with docker. With the assumption that docker is installed on your system and you have a working rails application with the PostgreSQL database. If you haven't installed Docker already, download it from the official docker website.

Create Dockerfile in the root folder of the project

Dockerfile describes all the steps that are required to create an image and would usually be contained within the root directory of the source code repository for your application.

# rails-app/cd rails-app/touch Dockerfile

First step of writing any Dockerfile is to choose the base image. We are going to use the official ruby docker image but a slim version.

FROM ruby:2.6.3-slim

Applying labels to images and containers allows you to add metadata via key/value pairs that can later be used to search for and identify Docker images and containers. You can see the labels applied to any image using the docker inspect command.

LABEL "maintainer"="viral@fakemail.com"LABEL "app_type"="backend"LABEL "service_type"="API"

Maintainer is a deprecated field in the Dockerfile specification.

Docker runs all processes within the container as root but it is recommended to use USER instruction to potentially avoid any security risks. Production containers should almost always be run under the context of a non-privileged user.

USER root

One of the most important instruction ENV allows you to set shell environment variables that can be used by your running application for configuration and during the build process to simplify the Dockerfile.

ENV APP /usr/src/appENV RAIL_ENV development

Now, we are going to introduce RUN instruction. We will be using these commands to create the file structure you need and install the required software dependencies. You will also be able to use shell environment variables defined in the previous step. Let's write a few RUN instructions required for our rails application.

RUN mkdir -p $APPRUN apt-get -y update && apt-get install -y --no-install-recommends \libgmp-dev \gcc make git \postgresql-client \dnsutils \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*

First instruction creates an app directory and second instruction runs few commands and installs require software, a Postgres client into a single step.

Remember that every instruction creates a new Docker image layer, so it often makes sense to combine a few logically grouped commands onto a single line. It is even possible to use the ADD instruction in combination with the RUN instruction to copy a complex script to your image and then execute that script with only two commands in the Dockerfile.

Next instruction, WORKDIR, is used to change the working directory in the image for the remaining build instructions.

WORKDIR $APP

COPY instruction copies file from the host machine to your container image.

COPY Gemfile $APP/GemfileCOPY Gemfile.lock $APP/Gemfile.lockRUN gem install bundler -v 1.17.3RUN bundle installCOPY . $APP

Install the required gems from Gemfile. Remember we are in the currently root folder of the app because of the WORKDIR instruction use.

This completes installing the required dependency. Now we only need to boot up the application and expose the public endpoint so that public internet can access our application. Before we do that, it is important to remove any unnecessary dependency to reduce our overall image size which eventually comes in handy when downloading and running applications.

RUN apt-get purge -y --auto-remove \gcc make git \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*

CMD instruction which defines the command that launches the process that you want to run within the container.

CMD [ "bundle", "exec", "puma", "-C", "config/puma.rb", "config.ru" ]

And finally, we use EXPOSE instruction to expose the application running process.

EXPOSE 3000

Here is your final Dockerfile looks like

FROM ruby:2.6.3-slimLABEL “maintainer”=“viral@fakemail.com”LABEL “app_type”=“backend”LABEL “service_type”=“API”USER rootENV APP /usr/src/appENV RAIL_ENV developmentRUN mkdir -p $APPRUN apt-get -y update && apt-get install -y —no-install-recommends \libgmp-dev \gcc make git \postgresql-client \dnsutils \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*WORKDIR $APPCOPY Gemfile $APP/GemfileCOPY Gemfile.lock $APP/Gemfile.lockRUN gem install bundler -v 1.17.3RUN bundle installCOPY . $APPRUN apt-get purge -y —auto-remove \gcc make git \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*CMD [ “bundle”, “exec”, “puma”, “-C”, “config/puma.rb”, “config.ru” ]

Build your Docker image by running,

docker build -t user/repo_name:tag .

But wait, before you can run this docker image locally, make sure your database YAML file is configured properly.

# database.yamldefault: &defaultadapter: postgresqlencoding: unicodehost: dbusername: postgrespassword:# For details on connection pooling, see Rails configuration guide# https://guides.rubyonrails.org/configuring.html#database-poolingpool: <%= ENV.fetch(“RAILS_MAX_THREADS”) { 5 } %>development:<<: *defaultdatabase: rails_docker_blog_development

Now copy this docker-compose file to your root folder of the project. And run docker-compose up -d --build

version: ‘3’services:db:image: postgresvolumes:- ./tmp/db:/var/lib/postgresql/dataweb:build: .command: bash -c “rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb config.ru”environment:- RAILS_ENV=developmentvolumes:- .:/usr/src/appports:- “3000:3000”depends_on:- db

Finally, you need to create the database. In another terminal, run:

docker-compose run web rake db:create

That’s it.

That’s it. Your app should now be running on port 3000 on your Docker daemon.

On Docker Desktop for Mac and Docker Desktop for Windows, go to http://localhost:3000 on a web browser to see the Rails Welcome.

If you are using Docker Machine, then docker-machine IP MACHINE_VM returns the Docker host IP address, to which you can append the port <Docker-Host-IP>:3000.

Originally published at https://viralpatel.blog

Follow us on Twitter 🐦 and Facebook 👥 and join our Facebook Group 💬.

To join our community Slack 🗣️ and read our weekly Faun topics 🗞️, click here⬇

If this post was helpful, please click the clap 👏 button below a few times to show your support for the author! ⬇

--

--