In Docker Compose, MongoDB keeps its data in a volume mounted at /data/db: the line mongo-data:/data/db under the service’s volumes. MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD, set together, create a root user in the admin database and start mongod with --auth. Without them, anyone who reaches the port can do anything.
The docker run command, converted
The image’s Docker Hub page shows the volume and the root user in two commands; together, with the tag pinned:
docker run --name some-mongo -v mongo-data:/data/db -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=secret -d mongo:9.0
services:
some-mongo:
image: mongo:9.0
volumes:
- mongo-data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: mongoadmin
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
mongo-data:
name: mongo-data
Use a named volume here rather than a host folder: the image’s documentation warns that on macOS and Windows, the file sharing Docker Desktop uses doesn’t work with MongoDB’s memory-mapped files. The page’s root-user example also passes --network some-network, which the converter would declare external: true. In Compose you can leave it out, since services share the project’s network and find each other by name.
A database the app waits for
services:
mongo:
image: mongo:9.0
environment:
MONGO_INITDB_ROOT_USERNAME: mongoadmin
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
- mongo-data:/data/db
ports:
- "127.0.0.1:27017:27017"
healthcheck:
test: mongosh --quiet --host "$$(hostname)" --eval "db.adminCommand('ping').ok"
interval: 5s
timeout: 5s
retries: 10
start_period: 20s
mongo-express:
image: mongo-express:1.0
ports:
- "127.0.0.1:8081:8081"
environment:
ME_CONFIG_MONGODB_URL: mongodb://mongoadmin:secret@mongo:27017/
ME_CONFIG_BASICAUTH_ENABLED: "true"
ME_CONFIG_BASICAUTH_USERNAME: admin
ME_CONFIG_BASICAUTH_PASSWORD: change-me
depends_on:
mongo:
condition: service_healthy
volumes:
mongo-data:
The check asks the container’s own hostname, not localhost. On first start the entrypoint runs a temporary mongod --bind_ip 127.0.0.1 to create the user and run the scripts in /docker-entrypoint-initdb.d, so a ping to localhost passes before the real server is up. The hostname resolves to the container’s network address, where only the final mongod --auth --bind_ip_all listens. ping needs no credentials, even with --auth. $$ is a literal $ for Compose, so the shell runs $(hostname).
Gotchas
- The root variables and the
.jsand.shinit scripts only act on an empty/data/db. Changing the password variable later changes nothing. - The root user lives in
admin. A connection string that names another database needs?authSource=admin, as inmongodb://mongoadmin:secret@mongo:27017/app?authSource=admin. docker compose down -vdeletesmongo-data.mongo:latestis 9.0 today. Pin the series: MongoDB upgrades one major version at a time, with the feature compatibility version set in between.