Skip to main content

·

Deploying MongoDB with Docker on DigitalOcean (Part 2: Replica Set — Same VPS)

PREFACE

Hello everyone! After gaining experience through my work, I started writing blog posts to share the knowledge and lessons I’ve learned from real-world challenges. My articles focus on Backend, Blockchain, and DevOps. I hope that by sharing what I’ve learned, I can also receive feedback from the community, improve my skills, and continue growing in the ever-expanding field of information technology.
After spending quite some time struggling to configure MongoDB with Docker, I decided to create this article series to share my knowledge and experience. The goal is to help you understand the underlying principles and easily apply them when setting up databases on DigitalOcean VPS instances for your own projects.
The series was initially planned to consist of two parts:
  • Part 1 (Standalone): Published some time ago, this part covers the basics of setting up a standalone MongoDB instance.
  • Part 2 (Replica Set — Same VPS): This is Part 2. It explains how to configure a MongoDB cluster on a single VPS to support load distribution and experimentation with MongoDB transactions.
However, I decided to add another part to explain in greater detail how to configure a MongoDB cluster across multiple VPS instances.
  • Part 3 (Replica Set — Multiple VPS): Coming soon! This part will cover configuring a MongoDB cluster across multiple VPS instances to support data backup and higher availability in case one VPS goes down.

INTRODUCTION

MongoDB Replica Set

A MongoDB Replica Set is a group of MongoDB services that maintain copies of the same dataset, keeping the data consistent and durable. In this article, I’ll focus on two concepts related to Replica Sets:
  • Transactions: Simply put, a transaction is a unit of work that must be completed reliably. Imagine transferring $50 from Account A to Account B. The operation must ensure that Account B receives $50 while Account A loses $50. MongoDB transactions help maintain ACID properties and ensure consistency across multiple documents.
  • CQRS: A design pattern that separates read and write operations. This separation can help distribute workloads and improve performance when interacting with data.
A basic Replica Set consists of several nodes. One node acts as the Primary, while the others act as Secondaries. If the Primary goes down, another Secondary can be promoted to Primary through an election.

Why Run a MongoDB Replica Set in Docker Containers?

Running a MongoDB Replica Set inside Docker containers makes it easier to maintain, back up, and scale your database infrastructure.

What Is the Core Principle Behind Setting Up a MongoDB Replica Set in Docker?

In Part 1 of this series, I explained how to run a standalone MongoDB instance inside a Docker container. In Part 2, our goal is to make the MongoDB containers communicate with one another and join the same Replica Set.
Before we begin, I’d like to explain the underlying approach so you can understand the main steps involved and adapt the configuration to suit your application.
To allow multiple MongoDB services to join the same Replica Set, we need two things:
  • A shared key for authentication.
  • A common network that allows the services to discover and communicate with one another.
Based on these two requirements, I’ll walk you through setting up a basic Replica Set with two nodes: one Primary and one Secondary.
With Docker containers, we can achieve these requirements through two approaches:
  • Build a custom base image containing the authentication key file, then use that image to build the MongoDB service containers.
  • Configure a subnet that allows the two containers to communicate with each other.
The following tutorial walks through the entire process step by step.

Tutorials

We’ll start by setting up the Primary node. Secondary nodes and arbiters can be configured in a similar way.

Step 1: Create the Setup Directory on Your VPS

The directory structure includes:
  • An empty /data directory to store data mounted from the Docker container onto the VPS host, helping preserve data when the container restarts.
  • An empty /log directory to store logs mounted from the Docker container onto the VPS host.
  • A /config directory containing:
    • mongod.conf: the MongoDB configuration file.
    • secret.kf: the authentication key file.
  • docker-compose.yaml: the Docker Compose configuration file for MongoDB.

Step 2: Create a Custom Base Image with a Dockerfile

Customize the base image using mongo:5.0.6 and configure the permissions for the key file so that the mongodb user inside the container can access it.
FROM mongo:5.0.6 RUN mkdir -p /var/log/mongodb && \ touch /var/log/mongodb/mongod.log && \ chown mongodb:mongodb -R /var/log/mongodb && \ touch /etc/secret.kf && \ chmod 400 /etc/secret.kf && \ chown mongodb:mongodb /etc/secret.kf CMD ["mongod"]

Step 3: Generate a Shared Authentication Key

Generate a random key file using OpenSSL:
openssl rand -base64 768 > secret.kf

Step 4: Configure MongoDB

The default configuration file is typically located at /etc/mongodb/mongod.conf.
We’ll create our own mongod.conf file and mount it into the Docker container, changing several settings from the defaults:
  • Enable authorization: Improve database security.
  • Configure key-file authentication: Set keyFile: /etc/secret.kf.
  • Configure the listening IP address: Set bindIp to 0.0.0.0 instead of 127.0.0.1 so that MongoDB can accept connections through interfaces beyond localhost. Access should still be restricted by an appropriate firewall.
  • Configure the Replica Set: Specify the Replica Set name.
# mongod.conf # For documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where to write logging data. systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # Where and how to store data. storage: dbPath: /data/db journal: enabled: true # engine: # wiredTiger: # How the process runs processManagement: # fork: true # Fork and run in the background # pidFilePath: /var/run/mongodb/mongod.pid # Location of the PID file timeZoneInfo: /usr/share/zoneinfo # Network interfaces net: port: 27017 bindIp: 0.0.0.0 # Binds to all IPv4 interfaces. # Restrict network access with firewall rules. security: authorization: "enabled" keyFile: /etc/secret.kf # operationProfiling: replication: replSetName: "marketplace_nft" # sharding: ## Enterprise-Only Options # auditLog: # snmp:

Step 5: Create a Subnet

Create a Docker network using the 10.5.0.0/24 subnet:
docker network create --subnet 10.5.0.0/24 marketplace

Step 6: Configure the Docker Compose File

Before starting the container, we need to mount the required files and directories. We’ll use a docker-compose.yaml file to define the container configuration.
We’ll also assign the MongoDB service a static IP address of 10.5.0.11.
This helps identify the Primary and Secondary nodes in the Replica Set. In particular, assigning static IP addresses within the Docker network prevents the nodes’ internal addresses from changing when containers are recreated.
version: "3.9" services: mongodb: image: mongodb_local:latest container_name: mongodb_01 ports: - "27019:27017" networks: outer: ipv4_address: "10.5.0.11" env_file: - .env volumes: - ./mongodb_configuration/:/docker-entrypoint-initdb.d/:ro - ./mongodb_configuration/init-mongodb.sh:/docker-entrypoint-initdb.d/init-mongodb.sh:ro - "./config/mongod.conf:/data/configdb/mongod.conf:ro" - "./config/secret.kf:/etc/secret.kf:ro" - "./data:/data/db" - "log:/var/log/mongodb" command: ["/usr/bin/mongod", "-f", "/data/configdb/mongod.conf"] restart: always volumes: log: null networks: outer: external: name: marketplace
The key settings in the Docker Compose file are:
  • image: Specifies the image used to create the container.
  • container_name: Sets the container's name.
  • ports: Maps a port on the host to a port inside the Docker container. Using a non-default external port can reduce exposure to basic automated scans, but it is not a substitute for proper access controls.
  • networks: Specifies the network and assigns the container the static IP address 10.5.0.11.
  • volumes: Mounts data and configuration between the VPS host and the container.
  • command: Specifies the command executed when the container starts.
  • restart: Configures the service to restart automatically when it stops, helping improve resilience.

Step 7: Build and Start the Docker Container

Run the following command to build and start MongoDB using the configuration above:
docker-compose up -d --build --force-recreate

Step 8: Check Whether the Container Is Running

docker ps

Step 9: Open a Shell Inside the Container

docker exec -it container_id bash
Replace container_id with the actual container ID or name.

Step 10: Set Up the MongoDB Database

  • Connect to MongoDB:
    • mongo
  • Switch to the admin database:
    • use admin
  • Create an administrator user:
    • db.createUser({ user: "superadmin", pwd: "REPLACE_WITH_A_STRONG_ADMIN_PASSWORD", roles: [{ role: "root", db: "admin" }] })
  • Create a user with readWrite privileges for the application database:
    • db.createUser({ user: "app_user", pwd: "REPLACE_WITH_A_STRONG_APPLICATION_PASSWORD", roles: [{ role: "readWrite", db: "database_test" }] })
Security note: Replace the example passwords with strong, unique credentials. Never publish real database passwords in a public article or repository. If previously exposed credentials are still in use, rotate them.

Step 11: Repeat the Steps to Create a Secondary Node

Repeat the setup steps to create the Secondary node.
Make sure to assign it a different IP address within the subnet you created earlier. For example, use 10.5.0.12 if the Primary node uses 10.5.0.11.

Step 12: Configure the Replica Set

  • Open a shell inside the Primary container:
    • docker exec -it container_id bash
  • Connect to MongoDB:
    • mongo
  • Authenticate as the administrator:
    • db.auth("superadmin", "REPLACE_WITH_A_STRONG_ADMIN_PASSWORD")
  • Configure the Replica Set by setting the Primary node's address and adding the Secondary node.
The original example uses the following commands:
rs.initial() cf = rs.conf() cf.members[0].host = "10.5.0.11:27017" rs.reconfig(cf) rs.add("10.5.0.12:27017")
Note: These commands are reproduced from the original tutorial's intended workflow. For a fresh MongoDB Replica Set, the usual initialization method is rs.initiate() rather than rs.initial(). The exact initialization and reconfiguration steps depend on whether the Replica Set has already been initialized.

Step 13: Configure the Firewall on the VPS

The original tutorial opens port 27016 with UFW:
sudo ufw allow 27016
Make sure the allowed port matches the host port configured in Docker Compose. In the example above, the mapping is 27019:27017, so the external port is 27019, not 27016.
For production deployments, avoid exposing MongoDB directly to the entire internet. Restrict access to trusted IP addresses, or use a private network, VPN, or SSH tunnel.

Step 14: Connect Using the MongoDB Connection URI

Once the configuration is complete, you can connect to MongoDB using a connection URI like this:
mongodb://app_user:REPLACE_WITH_A_STRONG_APPLICATION_PASSWORD@IP_VPS:27019/database_test?authSource=admin
Replace IP_VPS with your VPS's public IP address or hostname, and make sure the port matches your Docker Compose configuration and firewall rules.

Step 15: Leave a Like and Follow! ^^

CONCLUSION

This article has introduced the concepts and steps needed to set up a MongoDB Replica Set running inside Docker containers on a single VPS.
Here’s a quick recap of what we covered:
  • Core concepts: MongoDB Replica Sets and Docker.
  • The underlying approach: A simplified explanation of the problem and the key principles behind the solution.
  • Implementation: A step-by-step tutorial covering the configuration and deployment process.
  • Practical experience: Lessons and personal insights shared throughout the article.
I hope you find this guide useful. I’d love to hear your feedback, and I look forward to seeing you in Part 3 (Replica Set — Multiple VPS), where we’ll explore even more interesting configurations!
:))

Subscribe to the newsletter

Get new posts on Backend, Blockchain, DevOps, and life notes.

Loading Comments...