February 2

MongoDB Docker setup — Running MongoDB in Docker containers complete guide

Running MongoDB in Docker simplifies deployment and makes environments reproducible across development, testing and production. You can spin up a database in seconds without dealing with complex installation procedures. This guide covers everything from basic container setup to production configurations with replica sets, persistence, custom settings and proper backup strategies.

MongoDB in Docker

Why run MongoDB in Docker

Traditional MongoDB installation requires adding repositories, managing versions, and cleaning up when things break. Docker containers provide isolation and consistency that native installations struggle to match.

Benefits of containerized MongoDB

Docker containers bundle MongoDB with all dependencies into a single package. You get identical behavior on your laptop, CI pipeline, and production servers. The classic "works on my machine" problem disappears.

Containers start fast. Launching a fresh MongoDB instance takes about 5-10 seconds versus several minutes for traditional installation. This matters for integration tests and rapid development cycles.

Cleanup is simple. Delete the container and it's gone completely. No leftover config files, no orphaned data directories cluttering your system.

When Docker makes sense for MongoDB

Docker works well for development environments where quick setup and teardown matters. It's also solid for microservices architectures where each service might need its own database instance. CI/CD pipelines benefit significantly from reproducible database containers.

For production use, Docker adds a bit of complexity but provides consistency across environments. The performance overhead is typically 1-3% for database workloads, which most applications can easily absorb.

Quick start with Docker run

The fastest way to get MongoDB running is a single Docker command. This approach works for testing and development scenarios.

Basic container setup

Start MongoDB with minimal configuration:

docker run -d \
  --name mongodb \
  mongo:8

This starts MongoDB 8 in detached mode. The container runs until you stop it explicitly.

Check if it's running:

docker ps

Connect to the database:

docker exec -it mongodb mongosh

Environment variables for initial setup

MongoDB's Docker image supports environment variables for first-run configuration:

Variable

Description

Required

MONGO_INITDB_ROOT_USERNAME

Admin username

Optional

MONGO_INITDB_ROOT_PASSWORD

Admin password

Optional

MONGO_INITDB_DATABASE

Initial database name

Optional

Create an admin user on startup:

docker run -d \
  --name mongodb \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8

Connect with authentication:

docker exec -it mongodb mongosh -u admin -p secretpassword --authenticationDatabase admin

Exposing ports

MongoDB runs on port 27017 inside the container by default. Map it to your host:

docker run -d \
  --name mongodb \
  -p 27017:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8

Now you can connect from your host machine:

mongosh "mongodb://admin:secretpassword@127.0.0.1:27017/admin"

Use a different host port if 27017 is already in use:

docker run -d \
  --name mongodb \
  -p 27018:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8

Data persistence with volumes

Without volumes, your data vanishes when the container stops. That's acceptable for throwaway test databases, but anything beyond that needs persistence.

Named volumes

Docker named volumes are the simplest approach:

docker run -d \
  --name mongodb \
  -v mongodb-data:/data/db \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8

The volume mongodb-data persists even after you delete the container. List your volumes:

docker volume ls

Inspect volume details:

docker volume inspect mongodb-data

Bind mounts

Bind mounts map a host directory directly into the container. This is useful when you need direct access to data files:

docker run -d \
  --name mongodb \
  -v /path/to/data:/data/db \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8

Make sure the directory exists and has proper permissions. On Linux, the MongoDB user inside the container needs write access:

mkdir -p /path/to/data
chown -R 999:999 /path/to/data

The UID 999 corresponds to the MongoDB user inside the container.

Volume backup

Back up a named volume by running a temporary container:

docker run --rm \
  -v mongodb-data:/source:ro \
  -v $(pwd):/backup \
  alpine tar czf /backup/mongodb-backup.tar.gz -C /source .

This creates a compressed archive of the data directory. For proper database backups, use mongodump instead, which we'll cover later.

Docker Compose for MongoDB

Docker Compose makes multi-container setups manageable and keeps configurations under version control.

Basic compose file

Create a docker-compose.yml:

services:
  mongodb:
    image: mongo:8
    container_name: mongodb
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
      MONGO_INITDB_DATABASE: myapp
    ports:
      - "27017:27017"
    volumes:
      - mongodb-data:/data/db
    restart: unless-stopped

volumes:
  mongodb-data:

Start the service:

docker compose up -d

Stop and remove:

docker compose down

Remove including volumes:

docker compose down -v

Application with MongoDB

A typical setup includes your application and MongoDB together:

services:
  app:
    build: .
    environment:
      MONGODB_URI: mongodb://appuser:apppassword@mongodb:27017/myapp?authSource=admin
    depends_on:
      mongodb:
        condition: service_healthy
    ports:
      - "8080:8080"

  mongodb:
    image: mongo:8
    container_name: mongodb
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
    volumes:
      - mongodb-data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped

volumes:
  mongodb-data:

The depends_on with condition: service_healthy ensures your application waits for MongoDB to be ready before starting.

Custom configuration

Default settings work for development but production workloads often need tuning.

Configuration file mount

Create a custom configuration file mongod.conf:

storage:
  dbPath: /data/db
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2

systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true

net:
  port: 27017
  bindIp: 0.0.0.0

security:
  authorization: enabled

operationProfiling:
  slowOpThresholdMs: 100
  mode: slowOp

Mount it into the container:

docker run -d \
  --name mongodb \
  -v ./mongod.conf:/etc/mongod.conf:ro \
  -v mongodb-data:/data/db \
  -v mongodb-logs:/var/log/mongodb \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secretpassword \
  mongo:8 --config /etc/mongod.conf

Docker Compose with custom config

services:
  mongodb:
    image: mongo:8
    container_name: mongodb
    command: ["--config", "/etc/mongod.conf"]
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
    volumes:
      - mongodb-data:/data/db
      - mongodb-logs:/var/log/mongodb
      - ./mongod.conf:/etc/mongod.conf:ro
    ports:
      - "27017:27017"
    restart: unless-stopped

volumes:
  mongodb-data:
  mongodb-logs:

Common configuration options

Key settings to consider for production:

Setting

Default

Production recommendation

storage.wiredTiger.engineConfig.cacheSizeGB

50% of RAM - 1GB

Set explicitly based on available memory

operationProfiling.slowOpThresholdMs

100

Tune based on your performance requirements

net.maxIncomingConnections

65536

Set based on expected concurrent connections

security.authorization

disabled

Always enable in production

Verify your configuration is applied:

docker exec mongodb mongosh -u admin -p secretpassword --authenticationDatabase admin --eval "db.adminCommand({getParameter: '*'})"

Initialization scripts

The MongoDB Docker image can run scripts on first startup. This is useful for creating users, collections, and seed data.

JavaScript initialization

Place .js or .sh files in /docker-entrypoint-initdb.d/:

Create init/01-create-users.js:

db = db.getSiblingDB('myapp');

db.createUser({
  user: 'appuser',
  pwd: 'apppassword',
  roles: [
    { role: 'readWrite', db: 'myapp' }
  ]
});

db.createUser({
  user: 'readonly',
  pwd: 'readonlypassword',
  roles: [
    { role: 'read', db: 'myapp' }
  ]
});

Create init/02-create-collections.js:

db = db.getSiblingDB('myapp');

db.createCollection('users', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['email', 'createdAt'],
      properties: {
        email: {
          bsonType: 'string',
          description: 'must be a string and is required'
        },
        createdAt: {
          bsonType: 'date',
          description: 'must be a date and is required'
        }
      }
    }
  }
});

db.users.createIndex({ email: 1 }, { unique: true });

Mount the init directory:

services:
  mongodb:
    image: mongo:8
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
    volumes:
      - mongodb-data:/data/db
      - ./init:/docker-entrypoint-initdb.d:ro
    restart: unless-stopped

volumes:
  mongodb-data:

Scripts run in alphabetical order, only on first container start when the data directory is empty.

Shell script initialization

For more complex setup, use shell scripts:

Create init/00-setup.sh:

#!/bin/bash
set -e

mongosh <<EOF
use admin
db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD')

use myapp
db.createCollection('config')
db.config.insertOne({
  key: 'version',
  value: '1.0.0',
  createdAt: new Date()
})
EOF

Make it executable:

chmod +x init/00-setup.sh

Networking

Docker networking controls how containers communicate with each other and the outside world.

Default bridge network

Containers on the default bridge network can communicate via IP address but not hostname. For basic development this works fine:

docker run -d --name mongodb -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=pw mongo:8
docker run -it --rm mongo:8 mongosh "mongodb://admin:pw@$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mongodb):27017/admin"

Custom networks

Custom networks allow hostname-based communication:

docker network create myapp-network

docker run -d \
  --name mongodb \
  --network myapp-network \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=pw \
  mongo:8

docker run -it --rm \
  --network myapp-network \
  mongo:8 \
  mongosh "mongodb://admin:pw@mongodb:27017/admin"

The second container can reach MongoDB using hostname mongodb.

Compose networking

Docker Compose creates a network automatically. Services communicate by service name:

services:
  app:
    image: myapp
    environment:
      MONGODB_URI: mongodb://admin:pw@mongodb:27017/myapp?authSource=admin

  mongodb:
    image: mongo:8
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: pw

Health checks and monitoring

Proper health checks ensure containers are actually ready to serve traffic, not just running.

Basic health check

services:
  mongodb:
    image: mongo:8
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

Check health status:

docker inspect --format='{{.State.Health.Status}}' mongodb

Health check with authentication

When authentication is enabled, include credentials in the health check:

healthcheck:
  test: ["CMD", "mongosh", "-u", "admin", "-p", "secretpassword", "--authenticationDatabase", "admin", "--eval", "db.adminCommand('ping')"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

Monitoring with logs

View container logs:

docker logs mongodb

Follow logs in real-time:

docker logs -f mongodb

Limit output to recent entries:

docker logs --tail 100 mongodb

Enable profiling in your configuration to catch slow operations:

operationProfiling:
  slowOpThresholdMs: 50
  mode: slowOp

Mount a volume for logs:

volumes:
  - mongodb-data:/data/db
  - mongodb-logs:/var/log/mongodb

Backup strategies for Docker MongoDB

Data in containers needs the same backup discipline as traditional installations. Docker adds some considerations but the fundamentals remain.

Using mongodump in Docker

Run mongodump inside the container:

docker exec mongodb mongodump -u admin -p secretpassword --authenticationDatabase admin --out /dump
docker cp mongodb:/dump ./backup

For a specific database:

docker exec mongodb mongodump -u admin -p secretpassword --authenticationDatabase admin --db myapp --out /dump

Compressed backup directly to host:

docker exec mongodb mongodump -u admin -p secretpassword --authenticationDatabase admin --archive --gzip > backup.gz

Scheduled backups with cron

Create a backup script:

#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
CONTAINER="mongodb"

docker exec $CONTAINER mongodump \
  -u admin \
  -p "$MONGO_ROOT_PASSWORD" \
  --authenticationDatabase admin \
  --archive \
  --gzip > "$BACKUP_DIR/mongodb_${TIMESTAMP}.gz"

# Keep only last 7 days
find $BACKUP_DIR -name "mongodb_*.gz" -mtime +7 -delete

Add to crontab for daily 3 AM backups:

0 3 * * * /usr/local/bin/mongodb-backup.sh

Using Databasus for automated backups

Manual backup scripts work but require ongoing maintenance and lack built-in monitoring. Databasus (an industry standard for MongoDB backup) provides automated backups with a web interface, scheduling and notifications.

Install Databasus on a separate server using Docker:

docker run -d \
  --name databasus \
  -p 4005:4005 \
  -v ./databasus-data:/databasus-data \
  --restart unless-stopped \
  databasus/databasus:latest

Or with Docker Compose:

services:
  databasus:
    image: databasus/databasus:latest
    container_name: databasus
    ports:
      - "4005:4005"
    volumes:
      - databasus-data:/databasus-data
    restart: unless-stopped

volumes:
  databasus-data:

Access the web interface at http://your-databasus-server:4005, then:

  1. Add your database — Click "New Database", select MongoDB, enter your MongoDB server's connection details (host, port, credentials)
  2. Select storage — Choose local storage, S3, Google Cloud Storage, or other supported destinations
  3. Select schedule — Set backup frequency: hourly, daily, weekly, or custom cron expression
  4. Click "Create backup" — Databasus handles backup execution, compression, retention and notifications

Databasus supports multiple notification channels including Slack, Discord, Telegram and email, so you know immediately when backups succeed or fail.

Replica sets in Docker

For production environments, running MongoDB as a replica set provides high availability and data redundancy.

Single-node replica set

Even a single-node replica set is useful because it enables change streams and transactions:

services:
  mongodb:
    image: mongo:8
    container_name: mongodb
    command: ["--replSet", "rs0", "--bind_ip_all"]
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword
    ports:
      - "27017:27017"
    volumes:
      - mongodb-data:/data/db
    restart: unless-stopped

volumes:
  mongodb-data:

Initialize the replica set after starting:

docker exec -it mongodb mongosh -u admin -p secretpassword --authenticationDatabase admin --eval "rs.initiate()"

Three-node replica set

For actual high availability, run three nodes:

services:
  mongodb-primary:
    image: mongo:8
    container_name: mongodb-primary
    command: ["--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/mongodb/keyfile"]
    volumes:
      - mongodb-primary-data:/data/db
      - ./keyfile:/etc/mongodb/keyfile:ro
    networks:
      - mongodb-network
    restart: unless-stopped

  mongodb-secondary1:
    image: mongo:8
    container_name: mongodb-secondary1
    command: ["--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/mongodb/keyfile"]
    volumes:
      - mongodb-secondary1-data:/data/db
      - ./keyfile:/etc/mongodb/keyfile:ro
    networks:
      - mongodb-network
    depends_on:
      - mongodb-primary
    restart: unless-stopped

  mongodb-secondary2:
    image: mongo:8
    container_name: mongodb-secondary2
    command: ["--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/mongodb/keyfile"]
    volumes:
      - mongodb-secondary2-data:/data/db
      - ./keyfile:/etc/mongodb/keyfile:ro
    networks:
      - mongodb-network
    depends_on:
      - mongodb-primary
    restart: unless-stopped

networks:
  mongodb-network:

volumes:
  mongodb-primary-data:
  mongodb-secondary1-data:
  mongodb-secondary2-data:

Generate the keyfile for internal authentication:

openssl rand -base64 756 > keyfile
chmod 400 keyfile

Initialize the replica set:

docker exec -it mongodb-primary mongosh --eval "
rs.initiate({
  _id: 'rs0',
  members: [
    { _id: 0, host: 'mongodb-primary:27017', priority: 2 },
    { _id: 1, host: 'mongodb-secondary1:27017', priority: 1 },
    { _id: 2, host: 'mongodb-secondary2:27017', priority: 1 }
  ]
})
"

Security considerations

Running databases in containers doesn't reduce security requirements. If anything, you need more attention to configuration details.

Enable authentication

Never run MongoDB without authentication in any environment beyond local development:

services:
  mongodb:
    image: mongo:8
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secretpassword

Secure passwords with secrets

Use environment variables from secrets management:

services:
  mongodb:
    image: mongo:8
    environment:
      MONGO_INITDB_ROOT_USERNAME_FILE: /run/secrets/mongo_username
      MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongo_password
    secrets:
      - mongo_username
      - mongo_password

secrets:
  mongo_username:
    file: ./secrets/mongo_username.txt
  mongo_password:
    file: ./secrets/mongo_password.txt

Network isolation

Don't expose database ports to the public internet. Use internal Docker networks:

services:
  app:
    networks:
      - frontend
      - backend

  mongodb:
    networks:
      - backend

networks:
  frontend:
  backend:
    internal: true

Resource limits

Prevent runaway containers from consuming all system resources:

services:
  mongodb:
    image: mongo:8
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 4G
        reservations:
          cpus: "1"
          memory: 2G

Production checklist

Before running MongoDB Docker containers in production, verify these items:

  • Data persistence configured with volumes
  • Authentication enabled with strong passwords
  • Custom configuration tuned for workload
  • Health checks enabled
  • Automated backup strategy in place
  • Secrets managed securely (not hardcoded)
  • Network properly isolated
  • Resource limits set
  • Monitoring and alerting configured
  • Restart policy set to unless-stopped or always
  • Container image version pinned (not using latest)

Troubleshooting common issues

Container exits immediately

Check logs for errors:

docker logs mongodb

Common causes: permission issues on mounted volumes, corrupt data directory, or invalid configuration file syntax.

Permission denied on bind mount

Ensure the host directory has correct ownership:

sudo chown -R 999:999 /path/to/data

Or run MongoDB with your user ID:

docker run -d --user $(id -u):$(id -g) ...

Can't connect from host

Verify port mapping:

docker port mongodb

Check if MongoDB is listening on all interfaces. The default bind address should be 0.0.0.0 in Docker, but verify with:

docker exec mongodb mongosh --eval "db.adminCommand({getCmdLineOpts: 1})"

Replica set won't initialize

Ensure all nodes can resolve each other's hostnames. When using Docker Compose, services communicate by service name. If using custom hostnames, add them to /etc/hosts or use Docker's --add-host option.

Slow performance

Check if WiredTiger cache is sized correctly:

docker exec mongodb mongosh --eval "db.serverStatus().wiredTiger.cache"

For Docker Desktop on macOS and Windows, file system performance through volumes can be slow. Use named volumes instead of bind mounts for better performance.

Conclusion

Running MongoDB in Docker provides consistent environments across development, testing and production. Start with simple docker run commands for quick setups, then move to Docker Compose for more complex configurations. Always configure data persistence with volumes, set up proper health checks, and implement automated backups. The overhead of containerization is minimal compared to the operational benefits of reproducibility and isolation. Pin your image versions, tune your configuration for your workload, and treat container security with the same rigor as traditional deployments.