You set MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD in Compose, start the official mongo image, and your app logs MongoServerError: Authentication failed. The password is right. You can see it in the container's environment. Or you changed the password in .env, recreated the container, and now nothing can log in. Or the user your docker-entrypoint-initdb.d script should create never shows up.
This guide reproduces each of those failures with mongo:9.0.4 in Docker Compose and fixes them. Every error and log line below comes from that lab run on November 1, 2026, when mongo:latest pointed to the same 9.0.4 image digest. The questions behind these errors get a lot of traffic. On Stack Overflow, How can I create a database for a MongoDB container on startup? had 325,066 views that day and How to enable authentication on MongoDB through Docker? had 316,972. The image repo issue Initialization of a user defined database, username, and password using environment variables has 29 comments.
The short version:
- From another container,
localhostis that container. Connect to the Compose service name, for examplemongodb://mongo:27017. - The root user from
MONGO_INITDB_ROOT_USERNAMElives in theadmindatabase. If your connection string names another database, add?authSource=admin. - The
MONGO_INITDB_*variables and the/docker-entrypoint-initdb.dscripts only run when/data/dbis empty. Changing them later does nothing. Change the password withchangeUserPassword()or run the script by hand. - Percent-encode
@ : / ? # [ ] $in the password part of the connection string. -
docker logson the MongoDB container tells you why a login failed. Look forFailed to authenticate.
Fix Authentication failed with MONGO_INITDB_ROOT_USERNAME
Start with the map. Find the message you see in the left column:
The lab started with the smallest Compose file that turns on authentication:
services: mongo: image: mongo:9.0.4 environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD} volumes: - mongo-data:/data/db volumes: mongo-data: MONGO_ROOT_PASSWORD=Lab-Pass-4821 On the first start, the entrypoint ran a temporary mongod bound to 127.0.0.1, created the root user, shut it down and then started the real server as mongod --auth --bind_ip_all. A one-off mongo:9.0.4 container on the same Compose network played the app and ran mongosh 2.14.0. mongosh is built on the Node.js driver (the server log named driver 7.5.0), so Node.js apps get the same error names.
Connect to the service name, not localhost
client$ mongosh 'mongodb://localhost:27017' --eval 'db.runCommand({ping:1})' MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017 client$ mongosh 'mongodb://mongo:27017' --eval 'db.runCommand({ping:1})' { ok: 1 } Inside the app container, localhost points back at the app itself. Nothing listens on port 27017 there, so the connection is refused. Use the service name from your Compose file. The same rule applies to MySQL and Redis, as the MySQL guide and the Redis guide show.
Note that ping worked without a password. MongoDB lets unauthenticated clients run ping, but a real query failed with MongoServerError: Command find requires authentication. That message means you connected without credentials. Authentication failed. means you sent credentials and the server rejected them.
Add authSource=admin for the root user
When the password is right and the login still fails, check the database in the connection string first. Here it named the app database, and the right password failed:
client$ mongosh 'mongodb://root:Lab-Pass-4821@mongo:27017/appdb' --eval 'db.items.insertOne({a:1})' MongoServerError: Authentication failed. client$ mongosh 'mongodb://root:Lab-Pass-4821@mongo:27017/appdb?authSource=admin' --eval 'db.items.insertOne({a:1}).acknowledged' true A MongoDB user belongs to the database where it was created. The Docker Hub page for the image says MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD are for "creating a simple user with the role root in the admin authentication database". The MongoDB connection string docs describe authSource as the "Database name associated with the user's credentials. Defaults to defaultauthdb if specified, otherwise defaults to admin." So /appdb in the path made the driver look for root in appdb. Without a database in the path, as in mongodb://root:Lab-Pass-4821@mongo:27017/, the login worked because the default was admin.
The server log shows the difference between a wrong database and a wrong password. The client gets the same message for both:
$ docker logs mongolab-mongo-1 2>&1 | grep '"Failed to authenticate"' Failed to authenticate user=root db=appdb error=UserNotFound: Could not find user "root" for db "appdb" Failed to authenticate user=root db=admin error=AuthenticationFailed: SCRAM authentication failed, storedKey mismatch The raw lines are JSON. The lab filtered them with jq to show attr.user, attr.db and attr.error. UserNotFound means fix the authSource or the user name. storedKey mismatch means the user exists and the password is wrong.
Fix a password that changed in .env but not in MongoDB
Why MONGO_INITDB_ROOT_PASSWORD is ignored on restart
The lab changed MONGO_ROOT_PASSWORD in .env from Lab-Pass-4821 to New-Pass-7310 and ran docker compose up -d --force-recreate. The new container had the new value in its environment, but the new password failed:
The init step never ran. The entrypoint checks /data/db for files such as WiredTiger, journal and storage.bson, and skips initialization when any of them exist. The Docker Hub page says it directly: "Do note that none of the variables below will have any effect if you start the container with a data directory that already contains a database: any pre-existing database will always be left untouched on container startup."
So the old password stays valid until you change it inside MongoDB.
Change the password with mongosh
Log in with the old password and change it in place. Then the .env value and the database agree again:
client$ mongosh 'mongodb://root:Lab-Pass-4821@mongo:27017/?authSource=admin' \ --eval 'db.getSiblingDB("admin").changeUserPassword("root", "New-Pass-7310")' { ok: 1 } client$ mongosh 'mongodb://root:New-Pass-7310@mongo:27017/?authSource=admin' \ --eval 'db.runCommand({connectionStatus:1}).authInfo.authenticatedUsers' [ { user: 'root', db: 'admin' } ] If you lost the old password, or the volume only holds test data, docker compose down -v deletes the volume. In the lab, the next start ran the init step again and the new .env password worked. That also deletes every database in the volume.
A related trap: the lab started a volume with no auth variables, wrote data, then added MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD. The entrypoint still added --auth (the process ran as mongod --auth --bind_ip_all), but it did not create the user, because the volume was not empty. Every login failed with UserNotFound: Could not find user "root" for db "admin", and queries without credentials failed with Command aggregate requires authentication. MongoDB's localhost exception "allows you to create the first user or role in the system after enabling access control." It only works from inside the container, so the fix ran there:
docker exec mongolate-mongo-1 sh -c 'mongosh --quiet admin --eval "db.createUser({user: process.env.MONGO_INITDB_ROOT_USERNAME, pwd: process.env.MONGO_INITDB_ROOT_PASSWORD, roles: [\"root\"]}).ok"' It printed 1, and the root login worked. The old data was still there.
Fix docker-entrypoint-initdb.d scripts that don't run
Create the app user in the right database
Most apps should not log in as root. The usual pattern is a script in /docker-entrypoint-initdb.d that creates an app user. The lab mounted this file:
// Runs once, against MONGO_INITDB_DATABASE, only when /data/db is empty db.createUser({ user: "app", pwd: process.env.APP_DB_PASSWORD, roles: [{ role: "readWrite", db: "appdb" }], }); db.items.insertOne({ seeded: true }); With MONGO_INITDB_DATABASE: appdb and APP_DB_PASSWORD passed into the mongo service, the log showed running /docker-entrypoint-initdb.d/01-create-app-user.js and MongoDB init process complete; ready for start up. The .js file read the password with process.env, so it never sat in the script.
Now the authSource rule flips. The script ran in appdb, so the app user lives in appdb, not in admin:
client$ mongosh 'mongodb://app:App-Pass-5532@mongo:27017/appdb?authSource=admin' --eval 'db.items.countDocuments()' MongoServerError: Authentication failed. client$ mongosh 'mongodb://app:App-Pass-5532@mongo:27017/appdb' --eval 'db.items.countDocuments()' 1 Copying ?authSource=admin from a root example into an app user's connection string breaks it. Set authSource to the database the script ran in, or leave it out when that database is already in the path. The readWrite role on appdb also limits the user. A write to another database failed with MongoServerError: not authorized on other to execute command.
MONGO_INITDB_DATABASE only picks the database the .js scripts run against. The Docker Hub page notes that "MongoDB is fundamentally designed for "create on first use", so if you do not insert data with your JavaScript files, then no database is created." The insertOne line in the script creates appdb.
Run a new script on an existing volume
The lab then added 02-create-report-user.js and recreated the container on the same volume. The script did not run, and the user did not exist:
Init scripts follow the same rule as the root variables. They run once, on the first start of an empty /data/db. You don't need to delete the volume to apply a new one. Run it with mongosh inside the container, logged in as root, against the same database the entrypoint would use:
docker exec mongoinit-mongo-1 sh -c 'mongosh --quiet -u root -p "$MONGO_INITDB_ROOT_PASSWORD" \ --authenticationDatabase admin appdb /docker-entrypoint-initdb.d/02-create-report-user.js' After that, the report user logged in to appdb. The entrypoint also runs .sh files from the same folder, and ignores any other extension. On a fresh volume with no scripts, the log shows ignoring /docker-entrypoint-initdb.d/*.
Fix MongoParseError for passwords with special characters
Percent-encode the password in the connection string
The lab set the root password to p@ss:w/rd#1. The Compose value worked, because the entrypoint passes it to createUser and never builds a URL. The connection string was the problem:
client$ mongosh 'mongodb://root:p@ss:w/rd#1@mongo:27017/?authSource=admin' --eval 1 MongoRuntimeError: Unable to parse ss:w with URL client$ mongosh 'mongodb://root:pa:ss@mongo:27017/?authSource=admin' --eval 1 MongoParseError: Password contains unescaped characters client$ mongosh 'mongodb://root:pa@ss@mongo:27017/?authSource=admin' --eval 1 MongoParseError: Invalid connection string The MongoDB docs say that if the username or password includes $ : / ? # [ ] @, "those characters must be converted using percent encoding". Python's urllib.parse.quote("p@ss:w/rd#1", safe="") printed p%40ss%3Aw%2Frd%231, and that version logged in. Put the encoded form only in the connection string. The MONGO_INITDB_ROOT_PASSWORD value stays as typed. If the password contains $, Compose also treats it as a variable, which the MySQL guide covers.
Connect from the host through a published port
Without a ports entry, nothing on the host can reach MongoDB. The lab published 127.0.0.1:27018:27017, and mongosh with host networking connected to 127.0.0.1:27018 and printed 9.0.4. Port 27017 on the host was refused, because only 27018 was published. Binding to 127.0.0.1 keeps the port off the network, so only the host itself can connect.
A MongoDB Compose file that avoids these errors
compose.yaml, .env and the secret file
This file ran verbatim in the lab:
services: mongo: image: mongo:9.0.4 restart: unless-stopped environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongo_root_password MONGO_INITDB_DATABASE: appdb APP_DB_PASSWORD: ${APP_DB_PASSWORD} secrets: - mongo_root_password volumes: - mongo-data:/data/db - ./init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"] interval: 10s timeout: 5s retries: 5 start_period: 20s app: image: mongo:9.0.4 depends_on: mongo: condition: service_healthy environment: MONGODB_URI: mongodb://app:${APP_DB_PASSWORD}@mongo:27017/appdb?authSource=appdb command: ["sh", "-c", "mongosh --quiet \"$$MONGODB_URI\" --eval 'db.items.countDocuments()'"] secrets: mongo_root_password: file: ./secrets/mongo_root_password volumes: mongo-data: APP_DB_PASSWORD=App-Pass-5532 ./secrets/mongo_root_password holds the root password, and ./init/01-create-app-user.js is the script from above. The _FILE suffix is documented on Docker Hub for both root variables. With it, docker inspect on the container did not show the root password. Replace app with your real service and give it the MONGODB_URI. Keep APP_DB_PASSWORD URL-safe or percent-encode it in the URI.
The healthcheck only proves that mongod answers, since ping needs no login. It doesn't test the app's password. That is enough for depends_on. A wrong app password still shows up in the app's own log as Authentication failed. The Umami guide uses the same service_healthy pattern with PostgreSQL.
Check it
<code>text
$ docker compose up -d
Container mongofinal-mongo-1 Started
Container mongofinal-mongo-1 Healthy
Container mongofinal-app-1 Started
$ docker compose ps -a --format '{{.Service}} {{.Status}}'
app Exited (0) 7 seconds ago
mongo Up 18 seconds (healthy)
$ docker compose logs --no-log-prefix app
1
$ docker compose restart mongo
$ docker compose run --rm app
1
</code>
The log showed running /docker-entrypoint-initdb.d/01-create-app-user.js once, on the first start. Root logged in with the password from the secret file and ?authSource=admin. After the restart, the seeded document was still there. Keep the image pinned to an exact version such as mongo:9.0.4, so a restart never pulls a new major version onto your data volume. The PostgreSQL 17 to 18 guide shows what a planned major upgrade of a database volume looks like.
FAQ
Login errors
Why does MongoDB in Docker say Authentication failed when the password is right?
Check which database the user lives in. The root user from MONGO_INITDB_ROOT_USERNAME is in admin, so a URI like mongodb://root:pass@mongo:27017/appdb needs ?authSource=admin. Run docker logs on the MongoDB container. UserNotFound points to the database, storedKey mismatch to the password.
What is the default username and password for the MongoDB Docker image?
There is none. Without MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD, the lab's server ran without --auth, logged "Access control is not enabled for the database" and accepted writes without a login. If you set only one of the two, the container exits with error: missing 'MONGO_INITDB_ROOT_USERNAME' or 'MONGO_INITDB_ROOT_PASSWORD'.
How do I connect to MongoDB in Docker from the host?
Publish the port, for example ports: ["127.0.0.1:27017:27017"], and connect to 127.0.0.1 with ?authSource=admin for the root user. Without a published port, the host can't reach the container.
Setup and init scripts
Why are my docker-entrypoint-initdb.d scripts not running?
The volume already holds a database. The entrypoint runs the scripts only when /data/db has no data files. Run the script with mongosh inside the container, or delete the volume if the data doesn't matter. Only .js and .sh files run.
How do I create a database and user for a MongoDB container on startup?
Set MONGO_INITDB_DATABASE and mount a .js file into /docker-entrypoint-initdb.d that calls db.createUser() and inserts one document. MongoDB creates a database on the first write. The app then connects to that database without authSource=admin.
How do I change the MongoDB root password in Docker?
Changing MONGO_INITDB_ROOT_PASSWORD has no effect on an existing volume. Log in with the old password and run db.getSiblingDB("admin").changeUserPassword("root", "new-password"), then update .env to match.





