How and why to Dockerize your Rails app’s database

by Jason Swett,

Why to Dockerize your database

Dockerizing helps ease the pain of dependencies

Getting a new developer set up with a Rails app (or any app) can be tedious. Part of the tedium is the chore of manually installing all the dependencies: Ruby, RVM, Rails, PostgreSQL, Redis, etc.

It would be really nice if that developer could just run a single command and have all that app’s dependencies installed on their computer rather than having to install dependencies manually.

This ideal is in fact possible if you fully Dockerize your app. If you want to, you can create a Docker setup that will make it so you don’t have to install anything manually: no Ruby, no Rails, no RVM, no PostgreSQL, no Redis, nothing. It’s very nice.

Fully Dockerizing has drawbacks

Unfortunately, fully Dockerizing a Rails app isn’t without trade-offs. When working with a Dockerized app, there’s a performance hit, there are some issues with using binding.pry, and system specs/system tests in such a way that you can see them run in a browser is next to impossible.

None of these obstacles is insurmountable, but if you don’t want to deal with these issues, you can choose to Dockerize just some of your app’s dependencies instead of all of them.

Partial Dockerization

The Docker setup I use at work is a hybrid approach. I let Docker handle my PostgreSQL and Redis dependencies. I install all my other dependencies manually. This makes it so I don’t have to live with the downsides of full Dockerization but I still get to skip installing some of my dependencies. Any dependency I can skip is a win.

The example I’m going to show you shortly is an even simpler case. Rather than Dockerizing PostgreSQL and Redis, we’re only going to Dockerize PostgreSQL. I’m doing it this way in the interest of showing the simplest possible example.

Dockerizing for development vs. production

I want to add a note for clarity. The Docker setups I’ve been discussing so far are all development setups. There are two ways to Dockerize an app: for a development environment and for a production environment. Development environments and production environments of course have vastly different needs and so a different Docker setup is required for each. In a production environment we wouldn’t run PostgreSQL and a Rails server on the same machine. We’d have a separate database server instead. So I want to be clear that this Docker setup is for development only.

How to Dockerize your database

In order to Dockerize our database we’re going to use Docker Compose. Docker Compose is a tool that a) lets you specify and configure your development environment’s dependencies, b) installs those dependencies for you, and c) runs those dependencies.

Initializing the Rails app

Before we do anything Docker-related, let’s initialize a new Rails app that uses PostgreSQL.

$ rails new my_app -d postgresql

Adding a Docker Compose config file

Here’s the Docker Compose config file. It’s called docker-compose.yml and goes at the project root. This file, again, is what specifies our development environment’s dependencies. I’ve annotated the file to help you understand what’s what.

# docker-compose.yml
---
version: '3.8'

# The "services" directive lists all the services your
# app depends on. In this case there's only one: PostgreSQL.
services:

  # We give each service an arbitrary name. I've called
  # our PostgreSQL service "postgresql".
  postgres:

    # Docker Hub hosts images of common services for
    # people to use. The postgres:13.1-alpine is an
    # image that uses the Alpine Linux distribution,
    # very lightweight Linux distribution that people
    # often use when Dockerizing development environments.
    image: postgres:13.1-alpine

    # PostgreSQL has to put its data somewhere. Here
    # we're saying to put the data in /var/lib/postgresql/data.
    # The "delegated" part specifies the strategy for
    # syncing the container's data with our host machine.
    # (Another option would be "cached".)
    volumes:
      - postgresql:/var/lib/postgresql/data:delegated

    # This says to make our PostgreSQL service available
    # on port 5432.
    ports:
      - "127.0.0.1:5432:5432"

    # This section specifies any environment variables
    # that we want to exist on our Docker container.
    environment:
      # Use "my_app" as our PostgreSQL username.
      POSTGRES_USER: my_app

      # Set POSTGRES_HOST_AUTH_METHOD to "trust" to
      # allow passwordless authentication.
      POSTGRES_HOST_AUTH_METHOD: trust

volumes:
  postgresql:
  storage:

Next we’ll have to change config/database.yml ever so slightly in order to get it to be able to talk to our PostgreSQL container. We need to set the username to my_app and set the host to 127.0.0.1.

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

  # This must match what POSTGRES_USER was set to in docker-compose.yml.
  username: my_app

  # This must be 127.0.0.1 and not localhost.
  host: 127.0.0.1

development:
  <<: *default
  database: my_app_development

test:
  <<: *default
  database: my_app_test

production:
  <<: *default
  database: my_app_production
  username: my_app
  password: <%= ENV['DB_PASSWORD'] %>

init.sql

If we put a file called init.sql at the project root, Docker will find it and execute it. It’s necessary to have an SQL script that creates a user called my_app or else PostgreSQL will give us an error saying (truthfully) that there’s no user called my_app.

CREATE USER my_app SUPERUSER;

It’s very important that the init.sql file is in place before we proceed. If the init.sql file is not in place or not correct, it can be a difficult error to recover from.

Using the Dockerized database

Run docker-compose up to start the PostgreSQL service.

$ docker-compose up

Now we can create the database.

$ rails db:create

As long as the creation completed successfully, we can connect to the database.

$ rails db

Now we’re connected to a PostgreSQL database without having had to actually install PostgreSQL.

4 thoughts on “How and why to Dockerize your Rails app’s database

Leave a Reply

Your email address will not be published. Required fields are marked *