Bootstrap Ruby project in Docker
In the Ruby community, we have a lot of tools to help us to create a new project. But, if you are a beginner, you can be lost in the middle of all these tools. This code is a simple way to start a new project in Ruby using Docker
without Rails
, dip
or any other framework dependencies.
Great examples to start a new project in Ruby:
Development environment
Requirements
Installed Docker with BuildKit support and docker-compose are required:
Docker setup
This article will use Docker Compose to run the Ruby code. Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, you create and start all the services from the configuration with a single command.
Let’s build the image with the following docker/Dockerfile
where we can pass the RUBY_VERSION
, DEBIAN_CODENAME
as build arguments. The UID
and GID
are the neat parameters to configure file permissions for container volume that will be set in docker/docker-compose
. Another important thing is that we are using a non-root user (UID:GID -> app_user:app_group
) to run the application.
ARG RUBY_VERSION=3.1.2
ARG DEBIAN_CODENAME=bullseye
FROM ruby:$RUBY_VERSION-$DEBIAN_CODENAME
ENV APP_DIR=/app
ARG UID=1000
ARG GID=1000
RUN groupadd --gid $GID app_group \
&& useradd --no-log-init --uid $UID --gid $GID app_user --create-home \
\
&& mkdir -p $APP_DIR \
&& chown -R $UID:$GID $APP_DIR \
\
&& gem update bundler \
&& chown -R $UID:$GID /usr/local/bundle \
&& bundler --version
# copy files required for `bundle install` step
COPY --chown=$UID:$GID Gemfile* $APP_DIR/
# entry point setup
COPY --chmod=755 docker/entrypoint.sh /usr/bin/
# switching to app user
USER $UID:$GID
WORKDIR $APP_DIR
RUN echo "!!!!! Install gems !!!!!" \
&& bundle install -j "$(($(nproc)+1))"
ENTRYPOINT ["entrypoint.sh"]
CMD /bin/bash
Docker entrypoint script docker/entrypoint.sh
can be used to run commands as a non-root user on container start:
#!/bin/bash -e
export PATH="$PATH:/app"
bundle check || bundle install -j "$(($(nproc) + 1))"
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
In this article, we have a simple Docker environment with Ruby-only app
service. The docker-compose.yml
file is responsible for creating the containers and the docker/Dockerfile
is accountable for creating the image.
version: '3'
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
args:
RUBY_VERSION: 3.1.2
DEBIAN_CODENAME: bullseye
UID: 1000
GID: 1000
environment:
RAILS_ENV: development
volumes:
- .:/app:delegated
- bundle:/usr/local/bundle
stdin_open: true
tty: true
volumes:
bundle:
Gemfile
is copied to the container and installed gems. The bundle
volume is used to cache the gems.
# frozen_string_literal: true
source "https://rubygems.org"
gem "byebug"
We have a final file structure:
$ tree .
.
├── Gemfile
├── docker
│ ├── Dockerfile
│ └── entrypoint.sh
└── docker-compose.yml
To run app container in other OS versions, different user/group ids, build composed containers with docker-compose build --build-arg RUBY_VERSION="3.0.4" --build-arg DEBIAN_CODENAME="buster" --build-arg UID="1001" --build-arg GID="1001"
, re-build it docker-compose build --no-cache
. All the build arguments are optional.
The development environment can be started with
docker-compose run app
and then run any ruby commands.
The complete repository source code is available on docker-rails-bootstrap. Feel free to use it as a template for your projects. If you have any questions, feel free to ask them in the comments or reach out to me at @pocheptsov.
Next steps
In the following article, we will add Rails
app to the project and compare full-size solution with our skeleton bootstrap.