Setup a Ruby on Rails Application with Docker from Scratch

This is a basic development environment setup for Ruby on Rails application with MySQL using Docker and Docker Compose.

You can find the instructions on how to install Docker from here. If you are using Docker for Mac/Windows, then Docker Compose is already installed as a part of the desktop installs. If not, you can get the installation instructions here.


Getting Started: Creating a new Rails App

The method we’re going to use for generating a new Rails app is going to be quite different. In this example, I’m not going to require you from installing Ruby on your local machine. We will be running Ruby on a docker container with the command below:

docker run -it -v "$PWD:/app" -w /app --rm ruby:2.6 bash
-it This is actually 2 flags, -i and -t being combined. With these flags, we can run the image in interactive mode.
-v "PWD:/app" This will share the current folder ($PWD) with the folder inside the container which is /app.
-w /appThis will set the default working directory to /app
--rmThis flag means to remove/destroy the container when we exit the container.
ruby:2.6This will tell docker to use the Ruby 2.6 official image.
bashThis will let us start a bash session.

The command above will automatically pull the official image from the Docker Hub registry if the image does not exist in your local machine.

After running the command above, you will now be inside a container with Ruby 2.6 installed.

Next, we’re going to install the rails gem inside the Ruby container.

gem install rails

Once the installation is complete, we can now generate new rails application inside the container.

rails new blog -d mysql --skip-bundle

We’ll be using mysql as our database so we added the flag -d mysql. And we also told it to skip the process for running bundle install. We’ll do that later.

Since we already have our rails application, we can now exit the container by running:

exit

If you do a quick ls on your current directory, you can see the blog rails app that we just generated seconds ago.


Writing the Dockerfile

Now we can move into our blog app directory and create the Dockerfile.

cd blog && touch Dockerfile

Open the Dockerfile with the text editor of your choice and paste the following configuration.

FROM ruby:2.6

# Install the required packages and remove the apt cache.
RUN apt-get update -yqq \
  && apt-get install -yqq --no-install-recommends \
    default-mysql-client \
    nodejs \
  && rm -rf /var/lib/apt/lists

# Set your work directory
WORKDIR /app

# Set the default Yarn version to install
ARG YARN_VERSION=1.19.2

# Copy Gemfile to the container
COPY Gemfile* ./

# Install dependencies
RUN bundle install

# Copy everything in current directory (blog/) to the WORKDIR
COPY . .

# Install Yarn. This is needed for webpacker.
RUN curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION}
ENV PATH="/root/.yarn/bin:/root/.config/yarn/global/node_modules/.bin:$PATH"

# Expose port 3000 to allow us to access the site in our broser with localhost:3000
EXPOSE 3000

# Start rails server
CMD ["rails", "s", "-b", "0.0.0.0"]

Docker Compose configuration

Still inside the blog directory, we’ll now create the docker-compose.yml file.

touch docker-compose.yml

And add the configuration below:

version: "3"

services:
  db:
    image: mysql:5
    env_file: .env
    volumes:
      - mysql-data:/var/lib/mysql

  app:
    build: .
    env_file: .env
    volumes:
      - .:/app
    ports:
      - 3000:3000
    depends_on:
      - db

volumes:
  mysql-data:
    external: false

We have specified 2 services: db and app. These services will be running on their own separate containers. The db container will be running mysql, and the app container will be running the Rails app.

Let’s first look at the db service. We’ll create the .env file later, that’s going to be used on both services. We also specified volumes for the mysql data. This is to persist the mysql data, so that the data won’t disappear when we stop or destroy the containers.

As for the app service, note that we created a Dockerfile a while ago. With the build: . option, it will instruct it later to build the image using the Dockerfile we’ve created.

We also share the application source code using volumes, and set the ports. depends_on will instruct it to wait for the db service container to start first, before starting the app container.

Now let’s create the .env file which will contain the environment variables.

touch .env
MYSQL_HOST=db
MYSQL_ALLOW_EMPTY_PASSWORD=yes

Don’t forget to include .env to your .gitignore file, as you may not want to commit this file. I would recommend creating a sample file .env.example to give a hint for other developers on what is needed to be inside the .env file, and commit that instead of the actual .env file.

Next, we’ll have to edit our development database configuration in config/database.yml. We’ll set the host to use the environment variable.

// ...

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  host: <%= ENV['MYSQL_HOST'] %>

// ...

Build the image

Now we have the Dockerfile and the docker-compose.yml file. We can build the image using the command below:

docker-compose build

Initial Setup

These commands are only required when running the app for the first time.

Install and Setup Webpacker

docker-compose run --rm --no-deps app rails webpacker:install

Create the database

docker-compose run --rm --no-deps app rails db:create

Start up the containers

Now you can start up the containers simply by running the command below:

docker-compose up -d

Note: When you update something in Dockerfile, it is recommended to add the build flag (--build) to rebuild the image. Eg., docker-compose up -d --build

Open up your browser and visit http://localhost:3000

Rails App Default Landing Page

Now if you want run some other rails commands, you just have to run docker-compose run --rm --no-deps app [command]:

docker-compose run --rm --no-deps app rails g Home index

To stop all the containers:

docker-compose down
Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.