Dockerize your rails 7 app.

Hardik Vekariya
Simform Engineering
6 min readMar 30, 2023

Discover the usage of Docker within a Rails application to implement containers.

If you are new to building web applications using Ruby on Rails and want to learn how to containerize your application using Docker, then this beginner’s guide is for you. In this blog, we will introduce you to the concept of containerization and explain why it is a useful technique for modern software development. We will also walk you through the steps of creating a Dockerized Rails 7 app.

Docker is a widely-used containerization platform that empowers developers to create, bundle, and deploy applications consistently.

Many reasons why Docker is required are listed below:

  1. Portability: Developers can bundle applications and dependencies into Docker containers that run seamlessly on any machine or OS. It simplifies the movement of applications between different environments.
  2. Consistency: Docker uses Dockerfiles to construct containers uniformly, ensuring every container has the required dependencies.
  3. Scalability: Adding or removing containers horizontally is easy with Docker, allowing applications to handle increased traffic without manual intervention.
  4. Efficiency: Docker allows multiple containers to run on the same machine, decreasing hardware costs and infrastructure complexity.
  5. Security: Docker containers create an isolation layer between applications, which helps to prevent security breaches.

Overall, Docker provides powerful tools for developers to build, package, and deploy applications.

Setup Rails 7 app

rails new rails_7_with_docker --database=postgresql
cd rails_7_with_docker
rails db:create
rails server

It will start the rails 7 app at our http://localhost:3000 with Postgres as a database. For creating the rails 7 app, you must ensure you have the ruby 2.7.0+ version. If not, then please install a specific ruby version first.

Setup docker for the rails app

First, we have to create Dockerfile in our project directory to set up docker. Add Dockerfile to the root directory.

FROM ruby:3.0-bullseye as base

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

WORKDIR /docker/app

RUN gem install bundler

COPY Gemfile* ./

RUN bundle install

ADD . /docker/app

ARG DEFAULT_PORT 3000

EXPOSE ${DEFAULT_PORT}

CMD [ "bundle","exec", "puma", "config.ru"] # CMD ["rails","server"] # you can also write like this.

What is written above, let’s understand one by one.

  • Firstly it will fetch the ruby image from Dockerhub. I have used the ruby:3.0-bullseye image. You can explore more ruby images from Dockerhub.
  • Running dependencies will install the necessary dependencies to start a rails app.
  • Create a working directory inside docker.
  • Install bundler gem for installing other gem dependencies.
  • Add Gemfile and Gemfile. Lock inside our current working directory.
  • Run bundle installs for gem dependencies.
  • Add all our code of the current directory to docker’s current directory.
  • Define default_port 3000 as an argument.
  • Expose the port number so that this image will run on that port number.
  • And finally, we start our rails server using the cmd command, which takes an argument in a coma array separated form.

So now, we will build an image using this docker file.

Notes:- Now we are using a ruby image from dockerhub, so remove the ruby version from your gemfile; otherwise, it will throw a ruby version error.

docker build . -t rails_docker:1 # t flag use to give image name and version number.

After building an image, let’s start our container using created image

docker run -p 3000:3000 rails_docker:1

When we visit our local host, it will show an error like this.

This error comes out because it will not find any Postgres container within the same network. So, we will make the below changes in our database.yml file.

default: &default
adapter: postgresql
encoding: unicode
host: postgres
username: <%= Rails.application.credentials.dig(:db, :username) %>
password: <%= Rails.application.credentials.dig(:db, :password) %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

Here we are giving the hostname the same as the Postgres container name and adding your username and password for Postgres. So now we are going to rebuild the image.

docker build . -t rails_docker:2

We will create a Postgres container and start a rails container using the rails_docker:2 image.

docker network create rails_postgres # crete a network
docker run --network rails_postgres --name postgres -d -e POSTGRES_PASSWORD=postgres postgres:latest
docker run --network rails_postgres --name rails_7 -p 3000:3000 rails_docker:2
docker exec rails_7 rails db:create # create a database inside your docker project

Here we are creating a network name as rails_postgres so both rails and Postgres containers can run on the same network.

For the Postgres container, we must pass the environment variable “POSTGRES_PASSWORD” with the password and the image name; in this case, we used Postgres: latest. Here we gave the name to the container as Postgres using the — name flag so we can use that name in our database.yml file as a hostname.

Then we started a rails container with port 3000 and the rails_docker:2 image.

So now, when we visit our http://localhost:3000, we can see our rails app is up and running.

Boom!! You dockerized a rails project using Dockerfile with some manual setup for the multi containers.

Now we will understand how to create a multi-container app using the docker-compose.yml file.

Add the following code to create a docker-compose.yml/yml file in your root directory.

version: '3.7'
services:
db:
image: 'postgres:latest'
volumes:
- postgres:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres
# env_file:
# - ./env/postgres.env # you can also define env variables like this.
rails:
build: ./
ports:
- '3000:3000'
volumes:
- .:/docker/app # add our local code to dockerfile
depends_on:
- db # add all dependant container

volumes:
postgres: # named volume

Using docker-compose, we can now eliminate network creation (docker-compose automatically creates a network for containers) and many commands we wrote manually to run a multi-container. Now, we can add all code in a single file.

Let’s understand what we have done.

  • So firstly, we add the version number of the docker-compose file, which is mandatory to add.
  • Then we can define our container under services.
  • First, we defined the container name as db for the Postgres image. Here we provided an image and volume name for persisting a storage and environment variable.
  • The second container is for the image of the rail in which we first build a docker file which is simply a rails image. Then we define the port number, volumes, and depends_on(for a dependent container in this app, the rails app is dependent on the Postgres container).
  • And lastly, in the volumes section, we added all named volumes so that docker understands this is a named volume.

So now we made the Postgres container name as a db so that we will point a hostname as db in the database.yml file.

default: &default
adapter: postgresql
encoding: unicode
host: db
username: <%= Rails.application.credentials.dig(:db, :username) %>
password: <%= Rails.application.credentials.dig(:db, :password) %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
<<: *default
database: rails_7_with_docker_development

So now, our docker-compose.yml file is ready. Now it’s time to start a container using the below command.

docker-compose up # start a container

Visit http://localhost:3000/ and boom; your container is up and running!!

For creating a database, you can use,

docker-compose run rails rails db:create

For stopping containers, you can use,

docker-compose down # stop a container

Conclusion

Docker has become the industry standard for containerization and continues to evolve, with new features and capabilities regularly added.

Whether you’re a seasoned developer or just starting, Docker is an indispensable tool that can help you take your application development and deployment to the next level.

With these notes, we can wrap up our blog; thank you for reading this blog!!

Follow Simform Engineering to keep up with all the latest trends in the development ecosystem.

--

--