Skip to main content

Docker for Rails: Step-by-Step Guide to Dockerizing Rails with MongoDB

Learn to containerize your Ruby on Rails application with MongoDB using Docker. Simplify your deployment process and boost efficiency with Seta Workshop.

AgustĂ­n Vignolo
AgustĂ­n Vignolo May 9, 2024
Illustration - Docker for Rails

Consistency and scalability are essential in modern development.

Docker simplifies the deployment process by packaging applications into containers that can run reliably across different environments. While most guides focus on PostgreSQL with Rails, MongoDB is a powerful non-relational alternative that integrates seamlessly with Docker.

In this guide, we’ll walk you through the steps to dockerize a Rails app with MongoDB. Whether you’re already familiar with Docker or just getting started, this article will help you efficiently set up and run your app.

Ready to streamline your development process? Contact us for expert Dockerization services!

Dockerizing Rails and MongoDB

A few weeks ago, we took on the challenge of dockerizing a Rails app that uses MongoDB. While there are plenty of guides available for setting up Rails with Postgres, we found it surprisingly hard to find resources for non-relational databases like MongoDB.

This gap in available documentation inspired us to create this guide, so you don’t have to face the same obstacles.

Before we dive into the technical steps, let’s quickly review what Docker is and why it’s the ideal tool for this kind of project.

What is Docker?

Docker is an open-source platform that automates the deployment, scaling, and management of applications. It uses containerization technology to encapsulate apps and their dependencies into self-contained units that can run consistently across any system with Docker installed. For both local and remote deployments, Docker makes everything behave the same way.

Tired of setup issues? Discover how Dev Containers create a consistent environment for your Dockerized Rails app in our next article.

Advantages of Docker

  • Consistency Across Multiple Environments: Docker containers enable apps to behave the same way everywhere, from a developer’s personal computer to a test environment to a production server.
  • Isolation: Docker containers run separately from each other. This boosts security and lets you run multiple containers on one machine without them interfering with each other.
  • Portability: Since Docker containers encapsulate everything an application needs to run (including the operating system). They can run on any machine that has Docker installed, rno matter the underlying operating system.
  • Efficiency: Docker containers are lightweight and start quickly, making them much more efficient than virtual machines in terms of system resources.
  • Version control and component reuse: Docker has features for version control and component reuse, enabling you to track changes, understand differences, or revert to previous versions of a container. You can also reuse components from existing Docker images, saving time and reducing complexity.
  • Developer Productivity: Docker simplifies many aspects of software development, like setting up a development environment and managing dependencies. This can significantly boost developer productivity.

7 Steps to Containerizing Your Ruby on Rails App with MongoDB

Image created with DALL-E - Docker for rails: Step-by-step guide to Dockerizing Rails with MongoDB

Let’s dive into the details to get your Rails app containerized and running smoothly.

  1. Install Docker Desktop and prepare Ruby environment
  2. Create and configure the Dockerfile for your Rails app
  3. Define services (database and web app) in a compose.yml file.
  4. Build Docker images to containerize your app.
  5. Update the mongoid.yml file for MongoDB configuration.
  6. Start your containers with docker-compose up
  7. Access your app at localhost:3000.

Now, let’s break down each of these steps in detail to ensure a smooth process.

Step 1: Install Docker Desktop and prepare Ruby environment

  1. Install Docker Desktop: Start by downloading and installing Docker Desktop from Docker’s official page. This will install several tools that you could install separately, but for the purpose of this post, we recommend using the installer.
  2. Fetch the Ruby image: Use the Ruby image from Docker Hub. In this case, we’ll be using ruby:3.2-bullseye.
  3. Install dependencies: Install required dependencies for running a Rails project like Node.js as for compiling JavaScript assets in the asset pipeline.
  4. Create project directory: Make a working directory within the Docker file system for the project.
  5. Copy Gemfile and code: Copy the Gemfile and Gemfile.lock files into the Docker directory
  6. Install necessary gems: Run ‘bundle install’ to install the required gems.

⚠️ Tip: When using a Ruby image from Docker Hub, make sure the Ruby version in your Gemfile matches exactly with the one in the Docker container. Or, you can just remove the Ruby version from the Gemfile to avoid any version mismatch errors.

Step 2: Create and configure the Dockerfile for your Rails app

Now that you’ve set up Ruby, create a Dockerfile in the root directory of your project and create a file named Dockerfile without any extension.

Add the following application code to it, in order to configuring the environment:

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

Let’s break this configuration down 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.

By following these steps, you now have a Docker container configured to run your Rails app with Ruby and all required dependencies.

Step 3: Define services in a compose.yml file

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 setup, we will create a second configuration file.

A Dockerfile sets up the environment for a single Docker container, but it doesn’t handle the orchestration of multiple containers. In a typical app, you might have several services running in separate containers that need to talk to each other. This is where Docker Compose comes in.

  • Create a new file named compose.yml at the root of your project, just like 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 get 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 you having to manually create 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.

Note: When using depends_on: db, Docker enables the service named db to start before the web service. However, note that these services start asynchronously; one does not wait for the other to fully start before launching.

⚠️ Make sure the Rails server is accessible externally by using the -b ‘0.00.0’ flag in the start command. Without this, you might run into “address not found” errors.

Step 4: Build Docker images to containerize your app

To build your project using the configuration specified in the compose.yml file, 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 focus on this topic today).

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. Build a new image according to the instructions in the Dockerfile.
  3. Tag the new image with the name specified in the compose.yml file, if one is 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.

Step 5: Update the mongoid.yml file for MongoDB configuration

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, you must establish a new host for all Rails environments in the mongoid.yml file.

Below, we share 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

⚠️ Note: While you can configure this file before containers are built, it has been intentionally placed after for demo purposes.

Step 6: Start containers with docker-compose up

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

Now, it’s time to start your containers. To do so:

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

  2. 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.

Step 7: Access your app

After configuring MongoDB, you can access your application at localhost:3000 in your browser.

⚠️ Note: Keep in mind that the current MongoDB database is different from your initial one. After starting your app, you’ll need to reinitialize the database if you had any data stored before. You can do this with a seed task or through your app’s interface.

Taking advantage of the Docker client

There’s a better and much more powerful way to see logs and use specific terminal prompts from a desired container. Just go to your Docker client and pick the container that holds our web and database services.

There, you can choose each one and head over to the exec tab to get a respective command prompt.

The Docker Client

Inside the Docker client, we can see our compose container running, holding our web and database containers.

Docker client

The compose container logs

If we click the compose container (in this case, the my_project container), we can see the logs output from both web and database services.

Compose container console showing services logs

The web service console

Then, we can also click on a specific container service and use its own isolated console.

web service container console

⚠️ Note: If you push your image to Docker Hub, you can tell the web service to use the name_of_your_image_in_dockerhub and not build it locally but fetch it from the Hub instead. Just like we did with the ‘db’ service using the mongo:6 image.

All in all… Docker for Rails

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

With Docker, you can build, ship, and run your app anywhere, making your workflow more efficient and reliable. Dockerization makes it easier to scale, move, and manage dependencies for your Rails and MongoDB apps.

Partnering for your growth

Image by Unsplash. Partnering for your growth.

At Seta Workshop, we’re here to help you unlock the full potential of Docker.

As a nearshore software development company, we offer a unique blend of proximity and expertise to meet your needs. Our team of experts is ready to support your growth and streamline your development and deployment processes. Let’s work together to make your projects more efficient and scalable.

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

Let's build something
together!