Mongodb Docker Compose

MongoDB Docker Compose: A Complete Setup and Operations Guide

Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

Running MongoDB in a container is one of the fastest ways to get a working document database for local development or a small production service. This guide walks through a real mongodb docker compose configuration, from a minimal single-node setup to authentication, persistent storage, replica sets, and backups, so you have a working reference rather than a toy example.

MongoDB pairs naturally with Compose because the database, its configuration, and any supporting services (an admin UI, an app server, a replica-set init container) can all be described in one file and brought up with a single command. That’s the core appeal of a mongodb docker compose workflow: reproducibility. Anyone on the team runs docker compose up and gets the same database, same version, same configuration, every time.

Why Use MongoDB with Docker Compose

Before diving into YAML, it’s worth being clear about what problem this actually solves. Installing MongoDB natively on a laptop or server means managing a system service, dealing with OS-specific package quirks, and hoping the version matches what’s running in staging or production. A mongodb docker compose file sidesteps all of that:

  • The exact MongoDB version is pinned in the image tag, not “whatever the package manager installed.”
  • Configuration lives in version control alongside the application code, not scattered across /etc/mongod.conf on a machine nobody remembers setting up.
  • Tearing down and rebuilding the environment is a single command, which makes it trivial to test upgrades or reproduce a bug from a clean state.
  • Multiple services (MongoDB, a caching layer, the application itself) can be orchestrated together with defined startup order and shared networking.
  • This isn’t unique to MongoDB — the same reasoning applies to running Postgres in Docker Compose or Redis in Docker Compose — but MongoDB has a few of its own operational wrinkles (replica sets, its own authentication model, WiredTiger storage behavior) that are worth covering specifically.

    When Compose Is the Right Tool

    Docker Compose is well suited to local development, single-node staging environments, and small production deployments running on a single host. If you need multi-host orchestration, automated failover across machines, or horizontal scaling driven by a scheduler, you’re in Kubernetes territory instead — see our comparison of Kubernetes vs Docker Compose for where that line sits. For most teams, a single well-configured MongoDB container (or a small replica set of three containers) on one VPS is more than sufficient, and a lot simpler to operate.

    A Minimal MongoDB Docker Compose Setup

    Here’s a minimal, working mongodb docker compose file. It uses the official image, exposes the default port, and persists data to a named volume so container restarts don’t wipe your database.

    services:
      mongodb:
        image: mongo:7
        container_name: mongodb
        restart: unless-stopped
        ports:
          - "27017:27017"
        volumes:
          - mongo_data:/data/db
    
    volumes:
      mongo_data:

    Run it with:

    docker compose up -d

    Check that it’s healthy:

    docker compose ps
    docker compose logs mongodb

    Connect from the host using mongosh (or any MongoDB client) at mongodb://localhost:27017. That’s the whole minimal case — a single service, one volume, one port. Everything past this point is adding the pieces you actually need for anything beyond a throwaway sandbox.

    Adding Authentication to Your MongoDB Docker Compose File

    The minimal example above has no authentication at all, which is fine for a completely isolated local sandbox but not acceptable for anything reachable from a network. The official MongoDB image supports root-user bootstrapping through environment variables, which is the standard way to enable auth in a mongodb docker compose setup.

    services:
      mongodb:
        image: mongo:7
        container_name: mongodb
        restart: unless-stopped
        environment:
          MONGO_INITDB_ROOT_USERNAME: admin
          MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
        ports:
          - "27017:27017"
        volumes:
          - mongo_data:/data/db
    
    volumes:
      mongo_data:

    Note the ${MONGO_ROOT_PASSWORD} reference instead of a hardcoded password. That value should live in a .env file next to your compose file, which Compose reads automatically — never commit real credentials to version control. Our guide on managing Docker Compose environment variables covers this pattern in more depth, and if you need to store the credential itself more securely (rather than a plaintext .env value), Docker Compose secrets is the next step up.

    Restricting Network Exposure

    Once auth is in place, also reconsider whether MongoDB needs to be reachable from outside the Docker host at all. If only your application container talks to it, drop the ports mapping entirely and rely on Compose’s internal network — services on the same Compose network can reach each other by service name (mongodb:27017) without any port being published to the host. This is a meaningful security improvement with zero functional cost when nothing external needs a direct connection.

    services:
      mongodb:
        image: mongo:7
        restart: unless-stopped
        environment:
          MONGO_INITDB_ROOT_USERNAME: admin
          MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
        volumes:
          - mongo_data:/data/db
        networks:
          - backend
    
      app:
        build: .
        environment:
          MONGO_URI: mongodb://admin:${MONGO_ROOT_PASSWORD}@mongodb:27017
        depends_on:
          - mongodb
        networks:
          - backend
    
    networks:
      backend:
    
    volumes:
      mongo_data:

    Creating Application-Specific Users

    Using the root user for your application isn’t good practice. MongoDB’s image supports an initialization script directory (/docker-entrypoint-initdb.d) that runs once, on first container startup with an empty data directory, letting you create a scoped user for your actual application database:

    // init-mongo.js
    db = db.getSiblingDB('appdb');
    db.createUser({
      user: 'appuser',
      pwd: process.env.MONGO_APP_PASSWORD,
      roles: [{ role: 'readWrite', db: 'appdb' }],
    });

    Mount it as a volume:

        volumes:
          - mongo_data:/data/db
          - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro

    This script only runs when the data volume is empty — if you’re modifying user setup after the fact, you’ll need to remove the volume or apply the change manually via mongosh.

    Persisting Data Correctly

    Data persistence is where a lot of first-time mongodb docker compose setups go wrong. MongoDB stores its data files under /data/db inside the container by default, and that directory must be backed by a Docker volume — otherwise every docker compose down (or container recreation) wipes the database. The examples above already do this correctly with a named volume, but it’s worth understanding the alternatives:

  • Named volumes (mongo_data:/data/db) — Docker manages the storage location. This is the recommended default; it works consistently across host operating systems and is easy to back up with docker run --volumes-from.
  • Bind mounts (./data:/data/db) — you control the exact host path. Useful if you need direct filesystem access to the data files, but MongoDB’s WiredTiger storage engine can behave inconsistently with certain host filesystems (notably some network-mounted or non-POSIX-compliant filesystems), so test this carefully before relying on it in production.
  • tmpfs mounts — data lives in memory only, useful for ephemeral test databases that should never persist, never for anything you care about keeping.
  • Whichever you choose, confirm persistence actually works before trusting it: bring the stack up, insert a document, run docker compose down (not down -v, which deletes volumes), bring it back up, and confirm the document is still there.

    Running a MongoDB Replica Set with Docker Compose

    A single MongoDB instance is a single point of failure and, notably, transactions require a replica set even with just one member. Many applications that use MongoDB transactions need at least a single-node replica set even in development. Here’s a three-node replica set defined in one mongodb docker compose file:

    services:
      mongo1:
        image: mongo:7
        command: ["--replSet", "rs0", "--bind_ip_all"]
        volumes:
          - mongo1_data:/data/db
        networks:
          - mongo_cluster
    
      mongo2:
        image: mongo:7
        command: ["--replSet", "rs0", "--bind_ip_all"]
        volumes:
          - mongo2_data:/data/db
        networks:
          - mongo_cluster
    
      mongo3:
        image: mongo:7
        command: ["--replSet", "rs0", "--bind_ip_all"]
        volumes:
          - mongo3_data:/data/db
        networks:
          - mongo_cluster
    
    networks:
      mongo_cluster:
    
    volumes:
      mongo1_data:
      mongo2_data:
      mongo3_data:

    After bringing this up, initialize the replica set once from inside any of the containers:

    docker compose exec mongo1 mongosh --eval '
    rs.initiate({
      _id: "rs0",
      members: [
        { _id: 0, host: "mongo1:27017" },
        { _id: 1, host: "mongo2:27017" },
        { _id: 2, host: "mongo3:27017" }
      ]
    })'

    Health Checks and Startup Ordering

    Compose’s depends_on only waits for a container to start, not for MongoDB inside it to actually be ready to accept connections. For any service that depends on MongoDB being available (your app, or an init script), add a proper health check:

        healthcheck:
          test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
          interval: 10s
          timeout: 5s
          retries: 5

    Then reference it with depends_on: { mongodb: { condition: service_healthy } } on the dependent service so it actually waits for a real ready state rather than just container start.

    Backups and Debugging

    A running mongodb docker compose stack still needs a backup strategy — persistence against docker compose down doesn’t protect against disk failure, accidental docker compose down -v, or a bad migration. mongodump/mongorestore work the same way inside a container as anywhere else:

    docker compose exec mongodb mongodump --out /data/backup
    docker cp mongodb:/data/backup ./backup-$(date +%F)

    When something isn’t working, docker compose logs is the first place to look — the same debugging approach covered in our Docker Compose logs guide applies directly to MongoDB containers, including following logs live with -f and filtering by service name in a multi-container stack.

    If you need to rebuild the image after changing a Dockerfile that wraps the MongoDB base image (for example, to bake in custom config), see our Docker Compose rebuild guide for the difference between up --build and a full build --no-cache.


    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Does MongoDB in Docker Compose persist data by default?
    No. Without an explicit volume mounted at /data/db, all data is lost when the container is removed. Always define a named volume or bind mount for that path in any mongodb docker compose configuration you intend to keep data in.

    Can I run MongoDB and my application in the same Compose file?
    Yes, and it’s the standard pattern. Define both services in the same docker-compose.yml, put them on the same Compose network, and reference MongoDB from your app using the service name as the hostname (e.g., mongodb://mongodb:27017) rather than localhost.

    Why does my application fail to connect immediately after docker compose up?
    MongoDB takes a few seconds to initialize before it accepts connections, and Compose’s default depends_on doesn’t wait for that. Add a healthcheck to the MongoDB service and a condition: service_healthy dependency on the app service to fix race conditions at startup.

    Do I need a replica set for local development?
    Only if your application code uses MongoDB transactions or change streams, both of which require one. Otherwise, a single-node instance without --replSet is simpler and sufficient for most local development.

    Conclusion

    A solid mongodb docker compose setup starts minimal — one service, one volume, one port — and grows to include authentication, network isolation, and eventually a replica set as your requirements demand it. The key operational habits are the same regardless of scale: never skip the data volume, never hardcode credentials into the compose file, and verify persistence and health checks actually work before relying on them. For further reference on MongoDB’s own configuration options and replica set behavior, the official MongoDB documentation and Docker’s Compose file reference are both worth keeping bookmarked while you build this out. If you’re deploying this stack on your own server rather than a managed platform, a VPS provider like DigitalOcean is a reasonable place to host a single-node or three-node MongoDB Compose deployment.

    Comments

    Leave a Reply

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