Local MongoDB Replica Set Cluster for Real-Time Container Apps (with Mongo Express)

local-mongodb-replica-set-cluster-for-real-time-container-apps-(with-mongo-express)

Run a full 3-node MongoDB replica set locally for real-time containerized applications, with authentication, failover, and Mongo Express — all in one compose.yml.

If you’re developing containerized applications that need real-time MongoDB features like Change Streams, you’ll want a local replica set.

This guide shows you how to run a 3-node MongoDB cluster with authentication, automatic failover, and a built-in web interface — all in one compose.yml file.

I built this setup primarily for Podman, but it works with most container platforms, including Docker, with no changes.

You’ll get:

  • A 3-node MongoDB replica set — perfect for local development & real-time features
  • Keyfile-based authentication
  • Automatic replica set initialization
  • A configurable root admin user via .env
  • Mongo Express for browser-based DB management
  • Ready-to-use connection strings for primary and secondary nodes
  • Works with Docker & Podman

Why Use a Replica Set Locally?

Running a MongoDB replica set locally lets you:

  • Test high availability scenarios — simulate node failures
  • Work with real-time features like Change Streams
  • Develop apps that mirror production environments
  • Explore read preference configurations with primary and secondary nodes

The Setup

The cluster is defined in a single compose.yml that includes:

  • keyfile-generator – creates a secure keyfile for internal node authentication
  • mongo1, mongo2, mongo3 – the three replica set members
  • mongo-express – a web-based MongoDB admin interface

1 .env File

Create a .env file in the same directory:

MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=pass

Change these values to your preferred admin credentials.

2 compose.yml

Here’s the full configuration:

services:
  keyfile-generator:
    image: alpine:latest
    restart: "no"
    command: >
      sh -c "
        apk add --no-cache openssl &&
        if [ ! -f /data/keyfile ]; then
          openssl rand -base64 756 > /data/keyfile &&
          chmod 400 /data/keyfile;
        fi
      "
    volumes:
      - keyfile_data:/data

  mongo1:
    image: mongo:latest
    container_name: mongo1
    depends_on:
      - keyfile-generator
    restart: unless-stopped
    ports:
      - 27017:27017
    networks:
      - mongoCluster
    volumes:
      - mongo1_data:/data/db
      - mongo1_config:/data/configdb
      - keyfile_data:/etc/mongo-keyfile
    environment:
      - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
    command: >
      bash -c '
        echo "Starting MongoDB setup..."
        while [ ! -f /etc/mongo-keyfile/keyfile ]; do
          echo "Waiting for keyfile..."
          sleep 2
        done
        chmod 400 /etc/mongo-keyfile/keyfile
        mongod --replSet rs0 --bind_ip_all --keyFile /etc/mongo-keyfile/keyfile --fork --logpath /var/log/mongod.log
        for i in {1..30}; do
          if mongosh --quiet --eval "db.runCommand({ping: 1})" > /dev/null 2>&1; then
            break
          fi
          sleep 2
        done
        if ! mongosh --quiet --eval "rs.status()" > /dev/null 2>&1; then
          mongosh --eval "
            rs.initiate({
              _id: "rs0",
              members: [
                { _id: 0, host: "mongo1:27017", priority: 2 },
                { _id: 1, host: "mongo2:27017", priority: 1 },
                { _id: 2, host: "mongo3:27017", priority: 1 }
              ]
            })
          "
        fi
        for i in {1..60}; do
          if mongosh --quiet --eval "db.hello().isWritablePrimary" 2>/dev/null | grep -q true; then
            mongosh admin --quiet --eval "db.createUser({user:"${MONGO_INITDB_ROOT_USERNAME}",pwd:"${MONGO_INITDB_ROOT_PASSWORD}",roles:[{role:"root",db:"admin"}]})"
            break
          fi
          sleep 2
        done
        mongod --shutdown
        sleep 3
        exec mongod --replSet rs0 --bind_ip_all --auth --keyFile /etc/mongo-keyfile/keyfile
      '

  mongo2:
    image: mongo:latest
    container_name: mongo2
    depends_on:
      - keyfile-generator
    restart: unless-stopped
    ports:
      - 27018:27017
    networks:
      - mongoCluster
    volumes:
      - mongo2_data:/data/db
      - mongo2_config:/data/configdb
      - keyfile_data:/etc/mongo-keyfile
    command: >
      bash -c "
        chmod 400 /etc/mongo-keyfile/keyfile &&
        exec mongod --replSet rs0 --bind_ip_all --auth --keyFile /etc/mongo-keyfile/keyfile
      "

  mongo3:
    image: mongo:latest
    container_name: mongo3
    depends_on:
      - keyfile-generator
    restart: unless-stopped
    ports:
      - 27019:27017
    networks:
      - mongoCluster
    volumes:
      - mongo3_data:/data/db
      - mongo3_config:/data/configdb
      - keyfile_data:/etc/mongo-keyfile
    command: >
      bash -c "
        chmod 400 /etc/mongo-keyfile/keyfile &&
        exec mongod --replSet rs0 --bind_ip_all --auth --keyFile /etc/mongo-keyfile/keyfile
      "

  mongo-express:
    image: mongo-express:latest
    container_name: mongo-express
    depends_on:
      - mongo1
      - mongo2
      - mongo3
    restart: unless-stopped
    ports:
      - 8081:8081
    networks:
      - mongoCluster
    environment:
      - ME_CONFIG_MONGODB_ADMINUSERNAME=${MONGO_INITDB_ROOT_USERNAME}
      - ME_CONFIG_MONGODB_ADMINPASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
      - ME_CONFIG_MONGODB_URL=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0
      - ME_CONFIG_BASICAUTH_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
      - ME_CONFIG_BASICAUTH_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}

networks:
  mongoCluster:
    driver: bridge

volumes:
  mongo1_data:
  mongo1_config:
  mongo2_data:
  mongo2_config:
  mongo3_data:
  mongo3_config:
  keyfile_data:

3 Starting the Cluster

With Podman:

podman compose up -d

With Docker:

docker compose up -d

On first run:

  • A secure keyfile is generated
  • The replica set (rs0) is initialized
  • A root admin user is created
  • MongoDB restarts in auth mode
  • After the first Bootup is completed you may delete the keyfile-generator Container

Connecting to the Cluster

e.g. MongoDB Compass

Direct node connections:

  • Primary:
    mongodb://root:pass@localhost:27017/?authSource=admin&directConnection=true
  • Secondary 1:
    mongodb://root:pass@localhost:27018/?authSource=admin&directConnection=true
  • Secondary 2:
    mongodb://root:pass@localhost:27019/?authSource=admin&directConnection=true

(Replace root / pass if changed in .env)

Mongo Express UI

Access Mongo Express at:

http://localhost:8081

Login with your .env credentials.

Next Steps

  • Add TLS/SSL for encrypted connections
  • Implement Failover Logic between direct node connection strings in your app
  • Implement Change Streams in your app
  • Simulate node failures to test high availability
Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
adam-savage’s-tested:-the-challenge-of-installing-massive-and-culturally-sensitive-artifacts-(at-@airandspace)

Adam Savage’s Tested: The Challenge of Installing Massive and Culturally Sensitive Artifacts (at @airandspace)

Next Post
travelmate-ai:-real-time-ai-travel-planner-powered-by-redis-stack

TravelMate AI: Real-Time AI Travel Planner Powered by Redis Stack

Related Posts