Opinionated Docker Compose Setup
An opinionated Docker Compose for development with Rails 5.2+ and Postgres.
Used 52 times
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