Opinionated Docker Compose Setup

An opinionated Docker Compose for development with Rails 5.2+ and Postgres.
Icons/chart bar
Used 45 times
Created by
K Konnor Rogers

Usage

Purpose:


This is an opinionated Docker Compose setup with PostgresQL as the database for a Rails 6 app.
This is meant for development. This is not meant to be used in production.
This should ideally be forked and used at your own discretion. This is a starting point.

Things of note:


  • Ruby 2.6.x
  • Rails 5.2+
  • PostgresQL 12.x

Also, this container runs as a non-root user avoiding possible conflicts in permissions issues on Linux machines. (This is not an issue on Mac or Windows.)

Make sure you check the GROUP_ID & USER_ID in the "docker-compose.yml" file to make sure its the same as the user who will be running the container.

Prerequisites:


  • Docker
  • Docker-Compose

docker -v # => Docker version 19.03.8, build afacb8b7f0
docker-compose -v # => docker-compose version 1.25.0, build unknown

Usage:


If it's your first time running the container you can do:

docker-compose up --build

In all subsequent run you can do:

docker-compose up

This will spin up a docker container for you to access your app.

Adding a new gem:

docker-compose run --rm web bundle add <gem-name>
docker-compose down --remove-orphans
docker-compose up --build

Adding a new npm package:

docker-compose run --rm web yarn add <package-name>
docker-compose down --remove-orphans
docker-compose up --build

Fixing yarn integrity issues:

docker-compose run --rm web yarn install --check-files

Issues:

Ruby:


The official Ruby Docker images only support the latest PATCH version of each MINOR version of Ruby.

For example:

The only 3 supported Ruby Images on Docker right now are:

  • Ruby 2.5.8
  • Ruby 2.6.6
  • Ruby 2.7.1

This can create an issue with your Gemfile / .ruby-version in your project. Make sure you fix this accordingly.

PostgresQL:


 Also note, this project uses Postgresql 12. You will run into version mismatch issues if you have any other version of PostgresQL in your project.

In addition, if you decide later you want to change the PostgresQL user or password, you have to delete the volume associated in the "docker-compose.yml" file. 
By default, it is "db_data"

At full speed it would look something like:

docker volume rm <rails-directory>_db_data

Then you will have to rebuild the container:

docker-compose up --build

Bundler:


There may be version issues with bundler. You can adjust this accordingly in your "Dockerfile.dev".

I did my best to mitigate this issue by running "gem install bundler" prior to running "bundle install".



Run this command in your Rails app directory in the terminal:

rails app:template LOCATION="https://railsbytes.com/script/xkjspz"
Template Source

Review the code before running this template on your machine.

FILES = {
  DOCKERFILE: 'Dockerfile.dev',
  DOCKER_COMPOSE: 'docker-compose.yml',
  DOCKER_IGNORE: '.dockerignore',
  ENTRYPOINT_SCRIPT: 'entrypoint.sh',
  DATABASE_FILE: 'config/database.yml'
}.freeze

puts FILES.values
return if no?('The above files will be overwritten / created. Is this okay? (yes / no):')

run "touch #{FILES.values.join(' ')}"

RUBY_VERSION = '2.6'
APP_DIR = '/home/user/myapp'
USER_ID =  Process.uid || 1000
GROUP_ID = Process.gid || 1000
USER = 'user'

PORTS = { RAILS: '3000', WEBPACKER: '3035' }.freeze

POSTGRES_VERSION = "12.2"
DATABASE_USER = 'postgres'
DATABASE_PASSWORD = 'EXAMPLE'

create_file FILES[:DOCKERFILE] do
  <<~EOF
    # Pre setup stuff
    FROM ruby:#{RUBY_VERSION} as builder

    # Add Yarn to the repository
    RUN curl https://deb.nodesource.com/setup_12.x | bash && \\
        curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \\
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

    # Install system dependencies & clean them up
    # libnotify-dev is what allows you to watch file changes w/ HMR
    RUN apt-get update -qq && apt-get install -y \\
        postgresql-client build-essential yarn nodejs \\
        libnotify-dev && \\ 
        rm -rf /var/lib/apt/lists/*

    # This is where we build the rails app
    FROM builder as rails-app

    # Allow access to port 3000 & 3035
    EXPOSE #{PORTS[:RAILS]}
    EXPOSE #{PORTS[:WEBPACKER]}

    # This is to fix an issue on Linux with permissions issues
    ARG USER_ID=#{USER_ID}
    ARG GROUP_ID=#{GROUP_ID}
    ARG APP_DIR=#{APP_DIR}

    # Create a non-root user
    RUN groupadd --gid $GROUP_ID #{USER}
    RUN useradd --no-log-init --uid $USER_ID --gid $GROUP_ID #{USER} --create-home

    # Remove existing running server
    COPY entrypoint.sh /usr/bin/
    RUN chmod +x /usr/bin/#{FILES[:ENTRYPOINT_SCRIPT]}

    # fixing Permissions
    RUN mkdir -p $APP_DIR
    RUN chown -R $USER_ID:$GROUP_ID $APP_DIR

    # Define the user running the container
    USER $USER_ID:$GROUP_ID

    WORKDIR $APP_DIR

    # Install rails related dependencies
    COPY --chown=$USER_ID:$GROUP_ID Gemfile* $APP_DIR/

    # For webpacker / node_modules
    COPY --chown=$USER_ID:$GROUP_ID package.json $APP_DIR
    COPY --chown=$USER_ID:$GROUP_ID yarn.lock $APP_DIR

    # Fix an issue with outdated bundler
    RUN gem install bundler
    RUN bundle install

    # Copy over all files
    COPY --chown=$USER_ID:$GROUP_ID . .

    RUN yarn install --check-files


    ENTRYPOINT ["/usr/bin/#{FILES[:ENTRYPOINT_SCRIPT]}"]

    # Start the main process.
    CMD ["rails", "server", "-p", "#{PORTS[:RAILS]}", "-b", "0.0.0.0"]

  EOF
end

create_file FILES[:DOCKER_COMPOSE] do
  <<~EOF
    version: '3'

    services:
      web:
        environment:
          NODE_ENV: development
          RAILS_ENV: development
          WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
          POSTGRES_USER: #{DATABASE_USER}
          POSTGRES_PASSWORD: #{DATABASE_PASSWORD}

        build:
          context: .
          dockerfile: #{FILES[:DOCKERFILE]}
          args:
            USER_ID: #{USER_ID}
            GROUP_ID: #{GROUP_ID}
            APP_DIR: #{APP_DIR}

        command: bash -c "rm -f tmp/pids/server.pid &&
                          ./bin/webpack-dev-server --port #{PORTS[:WEBPACKER]} &
                          bundle exec rails server -p #{PORTS[:RAILS]} -b '0.0.0.0'"

        volumes:
          # make sure this lines up with APP_DIR above
          - .:#{APP_DIR}
          - node_modules:#{APP_DIR}/node_modules

        ports:
          - "#{PORTS[:RAILS]}:#{PORTS[:RAILS]}"
          - "#{PORTS[:WEBPACKER]}:#{PORTS[:WEBPACKER]}"

        depends_on:
          - db

      db:
        image: postgres:#{POSTGRES_VERSION}
        environment:
          POSTGRES_PASSWORD: #{DATABASE_PASSWORD}
        volumes:
          - db_data:/var/lib/postgresql/data


    volumes:
      db_data:
      node_modules:

  EOF
end

create_file FILES[:DOCKER_IGNORE] do
  <<~EOF
    # Ignore bundler config.
    /.bundle

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

    # Ignore pidfiles, but keep the directory.
    /tmp/pids/*
    !/tmp/pids/
    !/tmp/pids/.keep

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

    /public/assets
    .byebug_history

    # Ignore master key for decrypting credentials and more.
    /config/master.key

    /public/packs
    /public/packs-test
    /node_modules
    !/node_modules/.yarn-integrity
    /yarn-error.log
    yarn-debug.log*
    .git
  EOF
end

create_file FILES[:ENTRYPOINT_SCRIPT] do
  <<~EOF
    #!/bin/bash

    set -e

    # Remove a potentially pre-existing server.pid for Rails.
    rm -f /myapp/tmp/pids/server.pid

    # Then exec the container's main process (what's set as CMD in the Dockerfile).
    exec "$@"
  EOF
end

create_file FILES[:DATABASE_FILE] do
  <<~EOF
    default: &default
      adapter: postgresql
      encoding: unicode
      host: db
      username: <%= ENV['POSTGRES_USER'] %>
      password: <%= ENV['POSTGRES_PASSWORD'] %>
      pool: 5

    development:
      <<: *default
      database: myapp_development


    test:
      <<: *default
      database: myapp_test
  EOF
end
Comments

Sign up or Login to leave a comment.