First of all, why Docker?

At some point, I'm tired of installing these different applications on my mac. I need c++, python, ruby, mysql, postgresql etc all for different purposes. And once a while you will need a new version of all the packages. Having Docker removes the issue of having to dig into the code and fix every dependency once a while. Moreover, when I really need to fix something, since everything is documented in Dockerfile, it's easier for me to debug.

Here's the setup

For my simple Rails application, I only need two files, a Dockerfile to dockerize my application and a docker-compose.yml to config all the docker containers to run at development time. Here my Dockerfile:

FROM ruby:2.7.1

# this is required for installing yarn
RUN curl https://deb.nodesource.com/setup_12.x | bash
RUN curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update && apt-get install -y nodejs yarn postgresql-client

# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1

WORKDIR /usr/src/app

# install all gems
COPY Gemfile Gemfile.lock ./
RUN bundle install

# copy all the files to the working directory
COPY . .

RUN yarn install

This file basically installs node, yarn, postgresql-client, and all the required gems. Then, here's my docker-compose.yml:

version: "3.9"
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
  web:
    build: .
    ports:
      - "3000:3000"
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/usr/src/app
    depends_on:
      - db

This file boots up two containers. One for postgres and one for my Rails application. Something worth mentioning is that I mounted my root directory to /usr/src/app so that the Rails server will reflect any of my changes. This allows me to not rebuild and rerun the server every time I change something.

Oh, since postgresql is setup in a docker container, I need to set database.yml as follow:

default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: ${app_name}_development

Using plain text as password is as bad as one cound think but since this is only use in the development environment, it's fine.

Some more setup

Since it is the first time your application connects to the db, you will need to run the folloing to build:

docker-compose up --build

Then, in a different terminal window:

docker-compose run web rails db:create
docker-compose run web rails db:migrate

Lastly, how to boot them up?

Simply run:

docker-compose up

And if you want the taste of npm run dev, you can add the following to your Rackfile:

task :dev do
  system 'docker-compose up'
end

Then you can run rails dev like a charm.

Dependency Updates

Once a while you'll need to update your Gemfile or package.json, and you'll get the package not found error. That is because bocker containers are fixed after they're built. To rebuild, run docker-compose up --build web to force rebuild the web container. Things should work as expected afterwards.

Reference