on programming Rails for fun and profit...

Docked Rails: Setup a Ruby on Rails Application on Docker 🐳

Setting up a new Ruby on Rails application with all the dependencies doesn't have to be complicated. Docked is a new command line tool that simplifies the process of creating a new Rails app using Docker.

Docked Rails

Docked Rails CLI is a simple command-line tool for setting up a new Rails application with Docker. It provides a fantastic starting point for running a Rails application in a container, without worrying about installing specific versions of Ruby, Node.js, or SQLite. This frees you, the developer, to focus on application logic and makes it easy to deploy the containerized app.

At its heart, Docked is a very simple Dockerfile and a docked alias to interact with Docker. The docked alias allows you to run the common Rails commands, such as rails db:migrate, rails generate, or rails server in isolated containers.

Here's a list of all the topics this article covers:

This post assumes that you have already installed Docker and are familiar with its basic concepts, such as images, containers, and volumes. With that out of the way, let's focus on dockerizing a Rails application.

Step 1: Authenticate with Github Container Registry

Docked uses the Rails CLI image, which is hosted on GitHub Packages. Like Dockerhub, GitHub Packages is a platform for hosting and managing packages, including containers and other dependencies.

You can store and manage your Docker images in the GitHub Container Registry, which uses the package namespace https://ghcr.io. To access the Rails CLI image, you must authenticate to the GitHub container registry using a Personal Access Token.

The official documentation provides detailed instructions on creating personal access tokens. Once you have an access token, you need to log in to the registry.

First, save the token as an environment variable.

➜ export CR_PAT=YOUR_TOKEN

Next, sign in to the Container registry service at ghcr.io.

➜ echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
> Login Succeeded

To test successful authentication, try pulling the Rails CLI image from the registry.

➜ docker pull ghcr.io/rails/cli

If it works without any issues, then you're authenticated successfully. Move on to the next step.

Step 2: Create a volume to store the Gems

Containers are ephemeral—throwaway things that we spin up, use, and then delete.

To run a Rails app, we need to install all the required gems using the bundle install command. However, since we're running the Rails app in a container, and the container data doesn't persist when that container exits, we need to store these gems somewhere locally.

Docker uses volumes for persisting data generated by and used by containers. By mounting a volume in the directory where Bundler installs our gems, we can run bundler commands to populate and manage the gems on this volume. Hence it effectively becomes a local gem cache.

Let's create a new volume for the bundle gem cache using the volume create command.

➜ docker volume create ruby-bundle-cache

Ensure that the volume is created by listing all the volumes.

➜ docker volume ls

DRIVER    VOLUME NAME
local 	  ruby-bundle-cache

Step 3: Add Docked Alias

When using Docker, your Rails app runs within a Docker container and is isolated from your local computer. However, Docked provides a convenient way to run various commands against your application, such as arbitrary Ruby or Rails commands, bundler commands, Ruby Gem commands, and Node / NPM commands.

To interact with the Rails app, you need the following docker command (don't run the command yet).

➜ docker run --rm -it \
-v ${PWD}:/rails \
-v ruby-bundle-cache:/bundle \
-p 3000:3000 \
ghcr.io/rails/cli \
your-rails-command

This command creates a throwaway container and uses two volumes, one for the gem cache and another for the application code. Then it interactively runs a command you provide in the container's terminal.

Here's a short explanation of various parts of the command.

  • docker run: Runs a process in a container. We'll use it to run rails commands inside the container created with the Rails CLI image.
  • --rm: Automatically clean up the container and remove the file system when the container exits.
  • -it: Run an interactive bash session inside the container. This is a shorthand for the combination of --interactive (-i) and --tty (-t) flags.
  • -v ${PWD}:/rails: Mount the current working directory (returned by the PWD command) onto the /rails directory inside the container.
  • -v ruby-bundle-cache:/bundle: Mount the ruby-bundle-cache volume we created in step 2 onto the /bundle directory inside the container. We will learn how bundler will use this directory when we inspect the Dockerfile later.
  • -p 3000:3000: Publish the port 3000 on container on port 3000 on the host machine, i.e. on your laptop. This allows you to access the app on the browser.
  • ghcr.io/rails/cli: The dockerized Rails image, which we already pulled in step 1.
  • your-rails-command: The Rails command you want to run in the container, e.g. rails server or rails new.

Using docked Alias

Just reading this huge command hurts my eyes. Forget using it for all the Rails commands. Thankfully, you can use an alias to map this command to a simple word docked (you can use any term you want).

➜ alias docked='docker run --rm -it -v ${PWD}:/rails -v ruby-bundle-cache:/bundle -p 3000:3000 ghcr.io/rails/cli'

To make sure this is always available, add this to your shell configuration file in your home directory, such as ~/.zshrc or ~/.bashrc, and then restart your shell.

Once the shell alias has been configured, you can execute rails commands by simply typing docked.

➜ docked rails db:migrate

Much better, right? That's all the setup we need.

Step 4: Create and Run a Rails Application

It's time to create and run a real application inside a container. For this, type the usual rails new command with the docked alias.

➜ docked rails new weblog
➜ cd weblog

Now you can run any of the usual Rails commands to build your application.

➜ docked rails generate scaffold post title:string body:text
➜ docked rails db:migrate
➜ docked rails server

Let's run the app using the rails server command.

➜ docked rails server

That's it! Your app is now running inside a container. Access it in the browser at http://localhost:3000/posts.

If you want to go one step further and avoid having to type docked at all before each command, you can add the common commands as aliases:

➜ alias rails='docked rails'
➜ alias rails-dev='docked bin/dev'
➜ alias bundle='docked bundle'
➜ alias yarn='docked yarn'
➜ alias rake='docked rake'
➜ alias gem='docked gem'

Understanding the Dockerfile used by Docked

Let's take a look at the Dockerfile used by docked on GitHub; it's not that complicated. In this section, we'll try to understand each step in detail.

  • FROM ruby:3.2

Start from the Ruby 3.2 base image.

  • RUN curl -sL https://deb.nodesource.com/setup_19.x | bash -

Ensure node.js is available for apt-get.

  • RUN apt-get update -qq && apt-get install -y build-essential libvips nodejs && npm install -g yarn

Install dependencies necessary for running a Rails application. We need to run the update and install commands in the same line to avoid using stale package versions. This is because Docker caches the results of running each step (probably a topic for a separate blog post).

  • WORKDIR /rails

Mount the current working directory, in which the Rails app will be created, to the /rails directory in the container. From the official documentation,

The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile. If the WORKDIR doesn’t exist, it will be created even if it’s not used in any subsequent Dockerfile instruction.
VOLUME /bundle
RUN bundle config set --global path '/bundle' 
ENV PATH="/bundle/ruby/3.2.0/bin:${PATH}" 

The VOLUME instruction creates a mount point named /bundle and marks it as holding externally mounted volumes from the native host. Remember that we created a volume named ruby-bundle-cache in Step 2 and mounted it to /bundle in Step 3. This ensures gems are installed on a persistent volume and available as bins.

  • RUN gem install rails

The RUN instruction executes any commands provided. We use it to install the latest version of Rails in the resulting image.

  • ENV BINDING="0.0.0.0"

The ENV <key>=<value>  instruction sets the environment variable <key> to the value <value>. Here, we ensure binding is always 0.0.0.0, even in development, to access the server from outside the container.

  • ENTRYPOINT [""]

An ENTRYPOINT allows you to configure a container that will run as an executable. Here, we're overwriting the ruby image's entrypoint to provide an open command-line interface, so we can run any commands we want when creating and running a container.

Accessing the Docked Container CLI

Sometimes you may want to start a terminal session within your application's container. You may use the bash command to connect to your application's container. This allows you to inspect your application files and execute any arbitrary shell commands within the container.

weblog ➜ docked bash

root@ebbe23713775:/rails ls
Gemfile  Gemfile.lock  README.md  Rakefile  app  bin  config  config.ru  db  lib  log  public  storage	test  tmp  vendor

To start a new Rails console session, use the rails console command.

weblog ➜ docked rails console

Loading development environment (Rails 7.0.4.2)

irb(main):001:0> Post.count

  Post Count (2.1ms)  SELECT COUNT(*) FROM "posts"
=> 0

That concludes our exploration of the Docked CLI. I hope that you now have a working, containerized Rails application up and running.


I hope you found this article useful and that you learned something new.

If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.

Please subscribe to my blog if you'd like to receive future articles directly in your email. If you're already a subscriber, thank you.

Subscribe to Akshay's Blog

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe