In this article, we will focus on docker compose rails development and learn how to dockerize a ruby on rails application. In addition, we will containerize Postgres, Redis, and Sidekiq services by adding them to the docker-compose file.
All code from the article is available in my GitHub repository here.
Generate a Rails Application
First of all, before dockerizing rails 6 app let’s create an actual Ruby on Rails application. We will be using Rails in API mode and Ruby version 2.6.3
along with the Postgres database.
rvm use 2.6.3
rails new dockerize_rails_app --api -T -d postgresql
cd dockerize_rails_app
bundle install
Dockerize a Rails Application
Now we can move to docker rails.
Let’s add the Dockerfile.dev
file to the root of the project.
# Layer 0. Download base ruby image.
FROM ruby:2.6.3-slim
# Layer 1. Updating and installing the necessary software for the Web server. Cleansing to reduce image size.
RUN apt-get update -qq && apt-get install --no-install-recommends -y \
build-essential libpq-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Layer 2. Creating environment variables which used further in Dockerfile.
ENV APP_HOME /backend
# Layer 3. Adding config options for bundler.
RUN echo "gem: --no-rdoc --no-ri" > /etc/gemrc
# Layer 4. Creating and specifying the directory in which the application will be placed.
WORKDIR $APP_HOME
# Layer 5. Copying Gemfile and Gemfile.lock.
COPY Gemfile Gemfile.lock ./
# Layer 6. Installing dependencies.
RUN bundle check || bundle install --jobs 20 --retry 5
# Layer 7. Copying full application.
COPY . .
# Layer 8. Make file executable
RUN chmod +x ./dev-docker-entrypoint.sh
# Layer 9. Run migrations
ENTRYPOINT ["./dev-docker-entrypoint.sh"]
# Layer 10. Command to run application.
CMD ["rails", "s", "-p", "3000", "-b", "0.0.0.0"]
Next, we need to add the dev-docker-entrypoint.sh
file to the root of the project. We need this so that our database and all migrations are created automatically when we start the project.
#!/bin/sh
set -e
if [ -f tmp/pids/server.pid ]; then
rm tmp/pids/server.pid
fi
bundle check || bundle install
bundle exec rake db:create
bundle exec rake db:migrate
bundle exec rake db:seed
exec "$@"
And grant this file execution rights otherwise, we may enter into a permissions-related error.
chmod +x dev-docker-entrypoint.sh
Don’t forget to add the .dockerignore
file to the root of the project. .dockerignore
will speed up and simplify compilations by excluding large files from the context that are not used in the compilation.
/.bundle
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore uploaded files in development.
/storage/*
!/storage/.keep
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
coverage
.idea/*
.dockerignore
Adding Postgres to Docker Compose
We are now ready to add a docker-compose.yml
file which contains two services, the first is backend
which is our Ruby on Rails application and the second is db
which is our Postgres database.
version: '3.4'
x-backend:
&backend
build:
context: .
dockerfile: Dockerfile.dev
environment:
RAILS_ENV: development
DB_USERNAME: postgres
DB_PASSWORD: secret
DB_HOST: db
DB_PORT: 5432
DB_NAME: es_db
SECRET_KEY_BASE: STUB
restart: on-failure:3
stdin_open: true
tty: true
volumes:
- .:/backend:rw
- bundle_cache:/bundle
services:
backend:
<<: *backend
ports:
- 3000:3000/tcp
depends_on:
- db
db:
image: postgres:11.2
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
restart: always
volumes:
- postgres:/var/lib/postgresql/data
volumes:
bundle_cache:
postgres:
The last thing we want to do is update our config/database.yml
so that our database can be properly connected to our server.
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV["DB_USERNAME"] %>
password: <%= ENV["DB_PASSWORD"] %>
host: <%= ENV["DB_HOST"] %>
port: <%= ENV["DB_PORT"] %>
pool: <%= ENV["RAILS_MAX_THREADS"] { 5 } %>
development:
<<: *default
database: <%= ENV["DB_NAME"] %>_development
test:
<<: *default
database: <%= ENV["DB_NAME"] %>_test
production:
<<: *default
database: <%= ENV["DB_NAME"] %>_production
Now it’s time to start the project.
docker-compose up -d
Once all services are up and running, you can hit the http://localhost:3000 URL in your browser and see that your application is actually running.
Adding Redis and Sidekiq to Docker Compose
In this section, we will add the Docker Sidekiq and Docker Redis services. Because Sidekiq uses Redis as its database, we need to set up the Redis server details so that Sidekiq can connect to it. To do this, we need to add an environment variable to the Rails application.
REDIS_URL: redis://redis:6379/1
Fulldocker-compose.yml
with Sidekiq and Redis should look like this:
version: '3.4'
x-backend:
&backend
build:
context: .
dockerfile: Dockerfile.dev
environment:
RAILS_ENV: development
DB_USERNAME: postgres
DB_PASSWORD: secret
DB_HOST: db
DB_PORT: 5432
DB_NAME: es_db
SECRET_KEY_BASE: STUB
REDIS_URL: redis://redis:6379/1
restart: on-failure:3
stdin_open: true
tty: true
volumes:
- .:/backend:rw
- bundle_cache:/bundle
services:
backend:
<<: *backend
ports:
- 3000:3000/tcp
depends_on:
- db
db:
image: postgres:11.2
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
restart: always
volumes:
- postgres:/var/lib/postgresql/data
sidekiq:
<<: *backend
command: bundle exec sidekiq
depends_on:
- redis
- backend
redis:
image: 'redis:5.0-alpine'
command: redis-server
volumes:
- redis:/data
volumes:
bundle_cache:
postgres:
redis:
And the last important thing we need to add gem 'sidekiq'
to Gemfile
.
# Gemfile
gem 'sidekiq'
To test that all services are running, we need to restart docker-compose.
docker-compose stop
docker-compose up -d
By running:
docker-compose ps
You should see that all services are up and running.
List of Useful Docker Rails Commands
Here is a list of docker-compose commands that can help you develop Ruby on Rails, run RSpec, linters, or install gems.
Command | Alias | Description |
---|---|---|
docker-compose up | dcup | starts dev environment (all services) |
docker-compose stop | dcstop | stops dev environment (all services) |
docker-compose up backend | dcup backend | starts backend (API) only |
docker-compose up backend client | dcup backend client | starts both backend & client |
docker-compose ps | dcps | shows status of running containers |
docker-compose exec backend bash | dce backend bash | opens terminal inside container |
docker-compose exec backend rails c | dce backend rails c | opens rails console inside container |
docker-compose exec backend rubocop | dce backend rubocop | runs rubocop in backend container (you can use -a option to fix issues automatically) |
docker-compose exec backend {command} | dce backend {command} | to run any command inside a particular container |
docker-compose run backend {command} | dcr backend {command} | to run any command inside a particular container and to start container automatically |
- to use aliases
nano ~/.zshrc
and addplugins=(docker-compose ...)
Code from the article is here.
If you like it, check out my other article: Elasticsearch and Ruby on Rails Full Text Search With Autocompletion