Main page background image

Dockerize Rails App with Redis, Postgres and Sidekiq


Ihor T.
RoR Developer

In this article, we will focus on docker compose rails development and learn how to dockerize a ruby on rails application. In addition, we will containerize Postgres, Redis, and Sidekiq services by adding them to the docker-compose file.

All code from the article is available in my GitHub repository here.

Generate a Rails Application

First of all, before dockerizing rails 6 app let’s create an actual Ruby on Rails application. We will be using Rails in API mode and Ruby version 2.6.3 along with the Postgres database.

rvm use 2.6.3
rails new dockerize_rails_app --api -T -d postgresql
cd dockerize_rails_app
bundle install

Dockerize a Rails Application

Now we can move to docker rails.

Let’s add the Dockerfile.dev file to the root of the project.

# Layer 0. Download base ruby image.
FROM ruby:2.6.3-slim

# Layer 1. Updating and installing the necessary software for the Web server. Cleansing to reduce image size.
RUN apt-get update -qq && apt-get install --no-install-recommends -y \
  build-essential libpq-dev && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Layer 2. Creating environment variables which used further in Dockerfile.
ENV APP_HOME /backend

# Layer 3. Adding config options for bundler.
RUN echo "gem: --no-rdoc --no-ri" > /etc/gemrc

# Layer 4. Creating and specifying the directory in which the application will be placed.
WORKDIR $APP_HOME

# Layer 5. Copying Gemfile and Gemfile.lock.
COPY Gemfile Gemfile.lock ./

# Layer 6. Installing dependencies.
RUN bundle check || bundle install --jobs 20 --retry 5

# Layer 7. Copying full application.
COPY . .

# Layer 8. Make file executable
RUN chmod +x ./dev-docker-entrypoint.sh

# Layer 9. Run migrations
ENTRYPOINT ["./dev-docker-entrypoint.sh"]

# Layer 10. Command to run application.
CMD ["rails", "s", "-p", "3000", "-b", "0.0.0.0"]

Next, we need to add the dev-docker-entrypoint.sh file to the root of the project. We need this so that our database and all migrations are created automatically when we start the project.

#!/bin/sh
set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

bundle check || bundle install

bundle exec rake db:create
bundle exec rake db:migrate
bundle exec rake db:seed

exec "$@"

And grant this file execution rights otherwise, we may enter into a permissions-related error.

chmod +x dev-docker-entrypoint.sh

Don’t forget to add the .dockerignore file to the root of the project. .dockerignore will speed up and simplify compilations by excluding large files from the context that are not used in the compilation.

/.bundle

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development.
/storage/*
!/storage/.keep
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key
coverage
.idea/*
.dockerignore

Adding Postgres to Docker Compose

We are now ready to add a docker-compose.yml file which contains two services, the first is backend which is our Ruby on Rails application and the second is db which is our Postgres database.

version: '3.4'

x-backend:
  &backend
  build:
    context: .
    dockerfile: Dockerfile.dev
  environment:
    RAILS_ENV: development
    DB_USERNAME: postgres
    DB_PASSWORD: secret
    DB_HOST: db
    DB_PORT: 5432
    DB_NAME: es_db
    SECRET_KEY_BASE: STUB
  restart: on-failure:3
  stdin_open: true
  tty: true
  volumes:
    - .:/backend:rw
    - bundle_cache:/bundle

services:
  backend:
    <<: *backend
    ports:
      - 3000:3000/tcp
    depends_on:
      - db

  db:
    image: postgres:11.2
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
    restart: always
    volumes:
      - postgres:/var/lib/postgresql/data

volumes:
  bundle_cache:
  postgres:

The last thing we want to do is update our config/database.yml so that our database can be properly connected to our server.

# config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  username: <%= ENV["DB_USERNAME"] %>
  password: <%= ENV["DB_PASSWORD"] %>
  host: <%= ENV["DB_HOST"] %>
  port: <%= ENV["DB_PORT"] %>
  pool: <%= ENV["RAILS_MAX_THREADS"] { 5 } %>

development:
  <<: *default
  database: <%= ENV["DB_NAME"] %>_development

test:
  <<: *default
  database: <%= ENV["DB_NAME"] %>_test

production:
  <<: *default
  database: <%= ENV["DB_NAME"] %>_production

Now it’s time to start the project.

docker-compose up -d

Once all services are up and running, you can hit the http://localhost:3000 URL in your browser and see that your application is actually running.

Adding Redis and Sidekiq to Docker Compose

In this section, we will add the Docker Sidekiq and Docker Redis services. Because Sidekiq uses Redis as its database, we need to set up the Redis server details so that Sidekiq can connect to it. To do this, we need to add an environment variable to the Rails application.

  • REDIS_URL: redis://redis:6379/1 Full docker-compose.yml with Sidekiq and Redis should look like this:
version: '3.4'

x-backend:
  &backend
  build:
    context: .
    dockerfile: Dockerfile.dev
  environment:
    RAILS_ENV: development
    DB_USERNAME: postgres
    DB_PASSWORD: secret
    DB_HOST: db
    DB_PORT: 5432
    DB_NAME: es_db
    SECRET_KEY_BASE: STUB
    REDIS_URL: redis://redis:6379/1
  restart: on-failure:3
  stdin_open: true
  tty: true
  volumes:
    - .:/backend:rw
    - bundle_cache:/bundle

services:
  backend:
    <<: *backend
    ports:
      - 3000:3000/tcp
    depends_on:
      - db

  db:
    image: postgres:11.2
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
    restart: always
    volumes:
      - postgres:/var/lib/postgresql/data

  sidekiq:
    <<: *backend
    command: bundle exec sidekiq
    depends_on:
      - redis
      - backend

  redis:
    image: 'redis:5.0-alpine'
    command: redis-server
    volumes:
      - redis:/data

volumes:
  bundle_cache:
  postgres:
  redis:

And the last important thing we need to add gem 'sidekiq' to Gemfile.

# Gemfile
gem 'sidekiq'

To test that all services are running, we need to restart docker-compose.

docker-compose stop
docker-compose up -d

By running:

docker-compose ps

You should see that all services are up and running.

List of Useful Docker Rails Commands

Here is a list of docker-compose commands that can help you develop Ruby on Rails, run RSpec, linters, or install gems.

Command Alias Description
docker-compose up dcup starts dev environment (all services)
docker-compose stop dcstop stops dev environment (all services)
docker-compose up backend dcup backend starts backend (API) only
docker-compose up backend client dcup backend client starts both backend & client
docker-compose ps dcps shows status of running containers
docker-compose exec backend bash dce backend bash opens terminal inside container
docker-compose exec backend rails c dce backend rails c opens rails console inside container
docker-compose exec backend rubocop dce backend rubocop runs rubocop in backend container (you can use -a option to fix issues automatically)
docker-compose exec backend {command} dce backend {command} to run any command inside a particular container
docker-compose run backend {command} dcr backend {command} to run any command inside a particular container and to start container automatically
  • to use aliases nano ~/.zshrc and add plugins=(docker-compose ...)

Code from the article is here.

If you like it, check out my other article: Elasticsearch and Ruby on Rails Full Text Search With Autocompletion