Useful Docker Compose with Rails

David Weber
4 min readJun 12, 2018

--

Let’s dive into the practical side of docker compose and what you can do when you run docker-compose with your rails application. I’ve assumed you have already made a DockerFile. If not a sample is included at the bottom of the article.

To be overly brief, docker-compose will cluster a series of containers together for you and take care of all the networking. It allows you to communicate among containers defined in your compose file by name. Here is the docker-compose file we will reference throughout:

version: '3'services: app:  build: .  entrypoint: /bin/sh  command: rails s -b 0.0.0.0 -p 3000  ports:   - 3000:3000  volumes:   - .:/app  env_file: .env  environment:   - DEV_DATABASE_HOST=db   - DEV_MAIL_HOST=mailcatcher  depends_on:   - db  stdin_open: true  tty: true db:  image: postgres:latest  ports:   - 5432:5432  environment:   - POSTGRES_DB=database-dev   - POSTGRES_USER=root delayed_job:  build: .  command: rake jobs:work  volumes:   - .:/app   env_file: .env  environment:   - DEV_DATABASE_HOST=db   - DEV_MAIL_HOST=mailcatcher depends_on:  - db  stdin_open: true  tty: true mailcatcher:  image: zolweb/docker-mailcatcher:latest  ports:   - "1025:1025"    - "1080:1080"

There is a lot going on here. We will actually be creating four containers, each with a unique purpose and allowing them all to communicate with each other in the cluster (the networking is handled by docker-compose).

First is the application itself, app. It is going to run the rails app on port 3000, and the port mapping of 3000:3000 is how you will be able to access it. If you run the application you can reach it at localhost:3000 because your 3000 is mapped to the container’s 3000.

The env variables will be loaded from .env and it will have a volume for it’s filesystem. DEV_DATABASE_HOST will be the db container and DEV_MAIL_HOST will be the mailcatcher container that comes up later in the docker-compose up command. The app depends on the db and won’t come up until the db is ready.

The delayed_job container is essentially the same thing, but it will run the worker instead of the application. You can tell this from the cmd or command line.

The database container is very easy to get up and going. You don’t need to have an image for it because it is part of the docker registry. Simply defining the image as postgres:latest means that the image will be downloaded for the container with the most recent version of postgres. If you want a versioned postgres use the version instead of latest. It will run your postgres on 5432, the default postgres port. Once it is up and running you can connect to it the same way that you would the app container.

Mailcatcher is much the same way. If you are not familiar with mailcatcher go read up on it. But it is a simple application for monitoring and intercepting outgoing mail from your application. It even has a simple web based client so you can see what your outbound emails look like. The compose file will download and build the container for you and the 1080 and 1025 ports are mapped for you in docker compose file. These are required by mailcatcher.

Now for the magic. docker-compose up --build -d This will look at your compose file and build the containers (you don’t need to build each time you run them). It will also run them in daemon mode so you can attach to them at later times of your choosing. You may have wondered about the stdin_open and tty lines in the compose file. They will allow you to attach to a container and debug (byebug). To get your list of container names: docker ps To attach, pick the name of a container and docker attach #{container name} You will see logs as they come through.

You can also perform tasks on a given container by using docker-compose and providing the container name as defined in the docker compose. docker-compose exec #{docker-compose container name} #{command you want to execute} Some popular ones would probably be:

docker-compose exec app rake db:migrate

docker-compose exec app rails c

You can pick any of your containers and run relevant commands in them such as psql using the exec which is short hand for execute.

To stop and start your containers:

docker start/stop #{container as found by docker ps}

Otherwise you can use docker-compose to control the whole cluster:

docker-compose up/down

Whenever you make certain changes, like gem files or ruby version it’s normally a good idea to docker-compose up --build to rebuild your containers.

As promised here is a sample DockerFile for a rails application:

# Pick the ruby version for your rails app. This one is a rails 4 app and using ruby 2.2.3. Consider using 2.5.0 at least.
FROM ruby:2.2.3
# Installing some needed things here. Including ghostscript because this rails app works with pdfs. You may consider making adjustments.
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs && apt-get install -y ghostscript
# Make the directory for the app
RUN mkdir /app
# Set the working directory of everything to the directory we just made.
WORKDIR /app
# Copy the gemfile and gemfile.lock so we can run bundle on it
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock# Install and run bundle to get the app ready
RUN gem install bundler
RUN bundle install# Copy the Rails application into placeCOPY . .# Expose port 3000 on the container
EXPOSE 3000
# Run the application on port 3000
CMD rails s -b 0.0.0.0 -p 3000

Happy containering!

--

--