Dockerize your Rails app with MongoDB:

1. Create a Dockerfile in your project root for Ruby setup.
2. Define services (database and web app) in compose.yml.
3. Build Docker images.
4. Update mongoid.yml for database configuration.
5. Start containers with docker compose up.
6. Access your app at localhost:3000.

Initial challenge

A few weeks ago, I was tasked with "dockerizing" a Rails app that uses MongoDB. I quickly realized there aren't many guides available for this specific setup. There is ample documentation for integrating Postgres, but tutorials for non-relational databases are scarce. That's why I decided to write this simple guide...

... but first, let's do a brief overview to Docker.

Introduction to Docker

Docker is an open-source platform that automates the deployment, scaling, and management of applications. It uses containerization technology to encapsulate them and their dependencies into self-contained units that can run uniformly on any system with Docker installed.

First, go to the Docker page and download the Desktop tool. This will install several tools that you could install separately, but for the purpose of this post, we recommend using the installer.

Docker's file

Once you have installed the Desktop tool, navigate to your project's root directory and create a file named Dockerfile without any extension.

Add the following code to it:

FROM ruby:3.2-bullseye as base RUN apt-get update -qq && apt-get install -y build-essential apt-utils libpq-dev nodejs WORKDIR /docker/app COPY Gemfile* ./ RUN bundle install ADD . /docker/app

We will explain what that configuration does, going over it line by line:

  • FROM ruby:3.2-bullseye as base: Specifies the base image for subsequent instructions, which is Ruby 3.2 on the Debian "bullseye" release.

  • RUN apt-get update -qq && apt-get install -y build-essential apt-utils libpq-dev nodejs: Updates the package lists for upgrades and new installations, then installs essential packages for the Rails application. While not strictly necessary, it's beneficial to start with the latest versions.

  • WORKDIR /docker/app: Sets the working directory of the project to /docker/app in the Docker container. All subsequent commands will be run from this directory.

  • COPY Gemfile* ./: Copies the Gemfile and Gemfile.lock from your project to the current directory in the Docker container.

  • RUN bundle install: Installs the Ruby dependencies specified in the Gemfile.

  • ADD . /docker/app: Copies the entire current directory (your Rails application) into the /docker/app directory in the Docker container.

At this stage, we have a Docker container configured with the Rails web app. Now, we need to set up the MongoDB database to get it fully operational. According to the documentation, to have MongoDB working, you need to create another container and connect it to the Rails container via a Docker Network.

In order to avoid manual set up, we will create a second configuration file.

The composite

A Dockerfile defines the environment for a single Docker container, but it doesn't manage the orchestration of multiple containers. In a typical application, you might have several services running in separate containers that need to interact with each other. This is where Docker Compose comes in.

Create a new file named compose.yml at the root of your project, similar to the Dockerfile. This file describes the services that make up your app, in this case, a MongoDB database and a Rails web app.

The compose.yml file specifies how to obtain each service's Docker image: the database uses a pre-made MongoDB image, and the web app is built from the current directory. It also outlines the configuration needed to link these services together.

With Docker Compose, each service joins a default network, making them reachable and discoverable by other services on it. This means your Rails and MongoDB services can interact without manually creating a network.

Your new file will look like this:

services: db: container_name: my_database_container image: mongo:6.0 restart: always ports: - "27017:27017" web: container_name: my_web_container build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp ports: - "3000:3000" depends_on: - db # This responds to the service name, not the container's name.

Build your project

To build your project using the previous configuration, run:

./my_project: $ docker compose build

This command builds all the services defined in your compose.yml file that have a build context specified (if you use the -f flag you can specify another file, but we won't talk about it in this post). For each of these services, Docker performs the following steps:

  1. Use the specified Dockerfile to build each image. In our case, Docker will use the Dockerfile we created previously to build the web service.

  2. Builds a new image according to the instructions in the Dockerfile.

  3. Tags the new image with the name specified in the compose.yml file, if provided.

The result is a Docker image for each service that you can then start with the command docker compose up. If the images were already built and no changes were made to the Dockerfile or the build context, the existing images will be used.

Configure your database

If we were to run the web app in its current state, it would break immediately because the database configuration would be incorrect—it needs to point to the MongoDB container.

To access our database, we must establish a new host for all Rails environments in the mongoid.yml file.

Below is the code snippet where we set up the host for the development environment by specifying the name of the database service defined in the Docker Compose file:

development: clients: default: database: app_development hosts: - db:27017 # "db" is the name of MongoDB service in `compose.yml` we created in the previous section. options: server_selection_timeout: 1

Run your application

We have successfully configured our Ruby on Rails and MongoDB services within Docker containers, and corrected the MongoDB configuration inside the Rails app.

First, rebuild the containers using the build command mentioned earlier.

Next, to start the web service and its associated database service, execute the following command in your terminal:

./my_project: $ docker compose up

This will start both the web and database services. Once they are up, you can access your application at localhost:3000 in your browser.

All in all..

Dockerizing a Rails application with MongoDB can significantly streamline your development and deployment processes. By following the steps outlined in this guide, you can create a Dockerfile and a compose.yml file to define and manage your application's services. This approach ensures consistency across different environments and simplifies the setup process for new team members.

With Docker, you can build, ship, and run your application anywhere, making your workflow more efficient and reliable. Dockerization facilitates better scalability, portability, and dependency management for your Rails and MongoDB applications.

Ready to Dockerize your Rails app with MongoDB? Contact us for guidance and support!