/ Rails

Setting up Rails and Postgresql with Docker

More and more companies switch to using Docker in the development process. The easiness to set up the whole environment is really astonishing. That’s why I wanted to give you a quick recipe how to set up Rails and Postgresql containers to work with each other. This is especially useful when you hire a new developer who instead of setting the whole thing up on his own can depend on the configuration that is defined in one file and run it with one easy command.

If you didn’t see my previous article which takes you through a Rails application in Docker set up go and read it first, as this tutorial is a continuation.

So you have your Rails application set up. You can use your host’s Postgresql server for this application, but if you already have Docker installed it would be beneficial to have the database also run as a container. That way you can have separate server for each of your applications. This is especially useful when you develop multiple applications and each of them works on a different version of the database server.

Normally you would start a docker container with docker run command but this becomes to be tedious very quickly, when you need to pass more arguments to it (for example database set up). It gets even more complicated when your application needs some other services to like RabbitMQ, Elasticsearch or any other external service.

For that purpose we’ll use a handy tool which comes with the Docker - docker-compose. It will let you specify all the services and arguments to run your application.

Let’s get to it!

Go to your project’s root directory and create docker-compose.yml file.

version: '2'
services:
  hello_docker:
    build:
      context: .
      dockerfile: Dockerfile
    image: hello_docker
    ports:
      - 3000:3000
    links:
      - postgres:postgres
    environment:
      DATABASE_NAME: hello_docker
      DATABASE_USER: my_user
      DATABASE_PASSWORD: my_password
      DATABASE_HOST: postgres
    command: dockerize -timeout 30s -wait tcp://postgres:5432 bash -c "bundle exec rake db:migrate && bundle exec rails s"

  postgres_data:
    image: busybox
    container_name: postgresql_data
    volumes: 
      - /var/lib/postgresql/data
  
  postgres:
    image: postgres
    container_name: postgresql
    environment:
      POSTGRES_DB: hello_docker
      POSTGRES_USER: my_user
      POSTGRES_PASSWORD: my_password 
    volumes_from:
      - postgres_data

This is how docker-compose.yml should like. It starts three containers: our Rails application, Postgresql server and one data container for postgres data (thanks to that we won’t lose data when new postgres container is started).

One thing worth noting is

links:
  - postgres:postgres

This will make postgres container available in our Rails application container under postgres host i.e. when you run ping postgres from the Rails container you should get a positive response.

The other thing is command. This runs rails s through dockerize which is a handful tool to specify that we want to wait a while before starting Rails application. In this case we will wait at most 30 seconds for the Postgres server to boot up. This way we will avoid starting Rails application before the database server which would result in an error.

dockerize won’t work out of the box - you need to install it. To do so edit Dockerfile and add this somewhere at the top.

RUN apt-get install -y wget && \
   wget https://github.com/jwilder/dockerize/releases/download/v0.2.0/dockerize-linux-amd64-v0.2.0.tar.gz && \
   tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.2.0.tar.gz

OK! We are almost ready. Two more things to do.
a) You need to pass a database configuration to the database.yml. This is easy to do, because you can use erb there. This means you can execute Ruby in database.yml, and this means you have an access to the env variables.

Look at the docker-compose.yml file. We told our docker_hello container to set few envs like:
- DATABASE_NAME,
- DATABASE_USER,
- DATABASE_PASSWORD
- DATABASE_HOST.

They all hold information of the database connection.

Let’s use those in the database.yml

development:
  adapter: postgresql
  encoding: unicode
  database: <%= ENV["DATABASE_NAME"] %>
  pool: 5
  username: <%= ENV["DATABASE_USER"] %>
  password: <%= ENV["DATABASE_PASSWORD"] %>
  host: <%= ENV["DATABASE_HOST"] %>

test:
  adapter: postgresql
  encoding: unicode
  database: <%= ENV["POSTGRES_DB"] %>
  pool: 5
  username: <%= ENV["POSTGRES_USER"] %>
  password: <%= ENV["POSTGRES_PASS"] %>
  host: <%= ENV["POSTGRES_HOST"] %>

production:
  adapter: postgresql
  encoding: unicode
  database: <%= ENV["POSTGRES_DB"] %>
  pool: 5
  username: <%= ENV["POSTGRES_USER"] %>
  password: <%= ENV["POSTGRES_PASS"] %>
  host: <%= ENV["POSTGRES_HOST"] %>

b) You need to install pg gem. Add it into the Gemfile.

Now you need to rebuild the hello_docker image in order to copy new database.yml configuration, install dockerize, and pg gem.

After rebuilding the hello_docker image we are ready execute docker-compose up command which will start all the containers with a configuration specified in docker-compose.yml. Do it now.

If everything went as expected you should be able to go to localhost:3000 in your browser and see working Rails welcoming screen.

Setting up Rails and Postgresql with Docker
Share this