I am working with Docker and I have a stack with PHP, MySQL, Apache and Redis. I need to add MongoDB now so I was checking the Dockerfile for the latest version and also the docker-entrypoint.sh file from the MongoDB Dockerhub but I couldn't find a way to setup a default DB, admin user/password and possibly auth method for the container from a docker-compose.yml file.

In MySQL you can setup some ENV variables as for example:

db: image: mysql:5.7 env_file: .env environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} 

And this will setup the DB and the user/password as the root password.

Is there any way to achieve the same with MongoDB? Anyone has some experience or workaround?

4

13 Answers

Here another cleaner solution by using docker-compose and a js script.

This example assumes that both files (docker-compose.yml and mongo-init.js) lay in the same folder.

docker-compose.yml

version: '3.7' services: mongodb: image: mongo:latest container_name: mongodb restart: always environment: MONGO_INITDB_ROOT_USERNAME: <admin-user> MONGO_INITDB_ROOT_PASSWORD: <admin-password> MONGO_INITDB_DATABASE: <database to create> ports: - 27017:27017 volumes: - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro 

mongo-init.js

db.createUser( { user: "<user for database which shall be created>", pwd: "<password of user>", roles: [ { role: "readWrite", db: "<database to create>" } ] } ); 

Then simply start the service by running the following docker-compose command

docker-compose up --build -d mongodb 

Note: The code in the docker-entrypoint-init.d folder is only executed if the database has never been initialized before.

19

The docker hub mongo image will run any scripts in /docker-entrypoint-initdb.d/ when there is nothing populated in the /data/db directory.

Database Initialisation

The mongo container image provides the /docker-entrypoint-initdb.d/ path to deploy custom .js or .sh setup scripts that will be run once on database initialisation. .js scripts will be run against test by default or MONGO_INITDB_DATABASE if defined in the environment.

COPY mysetup.sh /docker-entrypoint-initdb.d/ 

or

COPY mysetup.js /docker-entrypoint-initdb.d/ 

A simple initialisation mongo shell javascript file that demonstrates setting up the container collection with data, logging and how to exit with an error (for result checking).

let error = true let res = [ db.container.drop(), db.container.createIndex({ myfield: 1 }, { unique: true }), db.container.createIndex({ thatfield: 1 }), db.container.createIndex({ thatfield: 1 }), db.container.insert({ myfield: 'hello', thatfield: 'testing' }), db.container.insert({ myfield: 'hello2', thatfield: 'testing' }), db.container.insert({ myfield: 'hello3', thatfield: 'testing' }), db.container.insert({ myfield: 'hello3', thatfield: 'testing' }) ] printjson(res) if (error) { print('Error, exiting') quit(1) } 

Admin User Setup

The environment variables to control "root" user setup are

  • MONGO_INITDB_ROOT_USERNAME
  • MONGO_INITDB_ROOT_PASSWORD

Example

docker run -d \ -e MONGO_INITDB_ROOT_USERNAME=admin \ -e MONGO_INITDB_ROOT_PASSWORD=password \ mongod 

or Dockerfile

FROM ENV MONGO_INITDB_ROOT_USERNAME admin ENV MONGO_INITDB_ROOT_PASSWORD password 

You don't need to use --auth on the command line as the docker entrypoint.sh script adds this in when it detects the environment variables exist.

15

UPD Today I avoid Docker Swarm, secrets, and configs. I'd run it with docker-compose and the .env file. As long as I don't need autoscaling. If I do, I'd probably choose k8s. And database passwords, root account or not... Do they really matter when you're running a single database in a container not connected to the outside world?.. I'd like to know what you think about it, but Stack Overflow is probably not well suited for this sort of communication.

Mongo image can be affected by MONGO_INITDB_DATABASE variable, but it won't create the database. This variable determines current database when running /docker-entrypoint-initdb.d/* scripts. Since you can't use environment variables in scripts executed by Mongo, I went with a shell script:

docker-swarm.yml:

version: '3.1' secrets: mongo-root-passwd: file: mongo-root-passwd mongo-user-passwd: file: mongo-user-passwd services: mongo: image: mongo:3.2 environment: MONGO_INITDB_ROOT_USERNAME: $MONGO_ROOT_USER MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongo-root-passwd MONGO_INITDB_USERNAME: $MONGO_USER MONGO_INITDB_PASSWORD_FILE: /run/secrets/mongo-user-passwd MONGO_INITDB_DATABASE: $MONGO_DB volumes: - ./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh secrets: - mongo-root-passwd - mongo-user-passwd 

init-mongo.sh:

mongo -- "$MONGO_INITDB_DATABASE" <<EOF var rootUser = '$MONGO_INITDB_ROOT_USERNAME'; var rootPassword = '$MONGO_INITDB_ROOT_PASSWORD'; var admin = db.getSiblingDB('admin'); admin.auth(rootUser, rootPassword); var user = '$MONGO_INITDB_USERNAME'; var passwd = '$(cat "$MONGO_INITDB_PASSWORD_FILE")'; db.createUser({user: user, pwd: passwd, roles: ["readWrite"]}); EOF 

Alternatively, you can store init-mongo.sh in configs (docker config create) and mount it with:

configs: init-mongo.sh: external: true ... services: mongo: ... configs: - source: init-mongo.sh target: /docker-entrypoint-initdb.d/init-mongo.sh 

And secrets can be not stored in a file.

A couple of gists on the matter.

8

Here's a working solution that creates admin-user user with a password, additional database (test-database), and test-user in that database.

Dockerfile:

FROM mongo:4.0.3 ENV MONGO_INITDB_ROOT_USERNAME admin-user ENV MONGO_INITDB_ROOT_PASSWORD admin-password ENV MONGO_INITDB_DATABASE admin ADD mongo-init.js /docker-entrypoint-initdb.d/ 

mongo-init.js:

db.auth('admin-user', 'admin-password') db = db.getSiblingDB('test-database') db.createUser({ user: 'test-user', pwd: 'test-password', roles: [ { role: 'root', db: 'test-database', }, ], }); 

The tricky part was to understand that *.js files were run unauthenticated. The solution authenticates the script as the admin-user in the admin database. MONGO_INITDB_DATABASE admin is essential, otherwise the script would be executed against the test db. Check the source code of docker-entrypoint.sh.

2

In case someone is looking for how to configure MongoDB with authentication using docker-compose, here is a sample configuration using environment variables:

version: "3.3" services: db: image: mongo environment: - MONGO_INITDB_ROOT_USERNAME=admin - MONGO_INITDB_ROOT_PASSWORD=<YOUR_PASSWORD> ports: - "27017:27017" 

When running docker-compose up your mongo instance is run automatically with auth enabled. You will have a admin database with the given password.

6

Given this .env file:

DB_NAME=foo DB_USER=bar DB_PASSWORD=baz 

And this mongo-init.sh file:

mongo --eval "db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD'); db = db.getSiblingDB('$DB_NAME'); db.createUser({ user: '$DB_USER', pwd: '$DB_PASSWORD', roles: [{ role: 'readWrite', db: '$DB_NAME' }] });" 

This docker-compose.yml will create the admin database and admin user, authenticate as the admin user, then create the real database and add the real user:

version: '3' services: # app: # build: . # env_file: .env # environment: # DB_HOST: 'mongodb://mongodb' mongodb: image: mongo:4 environment: MONGO_INITDB_ROOT_USERNAME: admin-user MONGO_INITDB_ROOT_PASSWORD: admin-password DB_NAME: $DB_NAME DB_USER: $DB_USER DB_PASSWORD: $DB_PASSWORD ports: - 27017:27017 volumes: - db-data:/data/db - ./mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh volumes: db-data: 

This works for me:

docker-compose.yaml

version: "3.8" services: mongodb: image: mongo:3.6 restart: always environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=hello volumes: - ./mongo-init/:/docker-entrypoint-initdb.d/:ro ports: - 27017:27017 - 9229:9229 mongo-express: image: mongo-express restart: always ports: - 8111:8081 environment: - ME_CONFIG_MONGODB_SERVER=mongodb - ME_CONFIG_MONGODB_ADMINUSERNAME=root - ME_CONFIG_MONGODB_ADMINPASSWORD=hello 

./mongo-init/init.js

conn = new Mongo(); db = conn.getDB("MyDatabaseName"); db.myCollectionName.createIndex({ "address.zip": 1 }, { unique: false }); db.myCollectionName.insert({ "address": { "city": "Paris", "zip": "123" }, "name": "Mike", "phone": "1234" }); db.myCollectionName.insert({ "address": { "city": "Marsel", "zip": "321" }, "name": "Helga", "phone": "4321" }); 

Look at the dashboard by

enter image description here

1

If you are looking to remove usernames and passwords from your docker-compose.yml you can use Docker Secrets, here is how I have approached it.

version: '3.6' services: db: image: mongo:3 container_name: mycontainer secrets: - MONGO_INITDB_ROOT_USERNAME - MONGO_INITDB_ROOT_PASSWORD environment: - MONGO_INITDB_ROOT_USERNAME_FILE=/var/run/secrets/MONGO_INITDB_ROOT_USERNAME - MONGO_INITDB_ROOT_PASSWORD_FILE=/var/run/secrets/MONGO_INITDB_ROOT_PASSWORD secrets: MONGO_INITDB_ROOT_USERNAME: file: secrets/${NODE_ENV}_mongo_root_username.txt MONGO_INITDB_ROOT_PASSWORD: file: secrets/${NODE_ENV}_mongo_root_password.txt 

I have use the file: option for my secrets however, you can also use external: and use the secrets in a swarm.

The secrets are available to any script in the container at /var/run/secrets

The Docker documentation has this to say about storing sensitive data...

You can use secrets to manage any sensitive data which a container needs at runtime but you don’t want to store in the image or in source control, such as:

Usernames and passwords TLS certificates and keys SSH keys Other important data such as the name of a database or internal server Generic strings or binary content (up to 500 kb in size)

Just a quick add-on to a couple of answers here - that are all great, it indeed saved me a lot of time figuring things out ! I would add a bit of cod if the user you want to create needs to be attached to a specific database

The main thing is that you need to be logged on your admin base to create new users. So with all the docker-compose.yml presented on the feed, your mongo-init.js file would look like :

db = db.getSiblingDB('admin'); // move to the admin db - always created in Mongo db.auth("rootUser", "rootPassword"); // log as root admin if you decided to authenticate in your docker-compose file... db = db.getSiblingDB('DB_test'); // create and move to your new database db.createUser({ 'user': "dbUser", 'pwd': "dbPwd", 'roles': [{ 'role': 'dbOwner', 'db': 'DB_test'}]}); // user created db.createCollection('collection_test'); // add new collection 

If that can help someone, I'm happy - cheers,

1

This is how I do it using env variables and secrets.

Following will create "app_user" and "app_database" on mongo container startup (only if used database stored in /data/db is empty).

Dockerfile

FROM mongo:5.0.3 COPY images/mongo/init.js /docker-entrypoint-initdb.d/ 

init.js

db.log.insertOne({"message": "Database created."}); db.createUser( { user: _getEnv("MONGO_USER"), pwd: cat(_getEnv("MONGO_PASSWORD_FILE")), roles: [ "readWrite", "dbAdmin" ] } ); 
  • Script will use MONGO_INITDB_DATABASE (env variable defined in docker-compose.yml) for database name.
  • Scripts in /docker-entrypoint-initdb.d/ will only be run if used database stored in /data/db is empty.

docker-compose.yml

mongo: build: context: . dockerfile: ./images/mongo/Dockerfile container_name: app_mongo environment: MONGO_INITDB_DATABASE: "app_database" MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/app_mongo_root_password MONGO_INITDB_ROOT_USERNAME: "root" MONGO_PASSWORD_FILE: /run/secrets/app_mongo_password MONGO_USER: "app_user" secrets: - app_mongo_password - app_mongo_root_password volumes: - mongo:/data/db secrets: app_mongo_password: file: ./secrets/app_mongo_password.txt app_mongo_root_password: file: ./secrets/app_mongo_root_password.txt volumes: mongo: 
2

My answer is based on the one provided by @x-yuri; but my scenario it's a little bit different. I wanted an image containing the script, not bind without needing to bind-mount it.

mongo-init.sh -- don't know whether or not is need but but I ran chmod +x mongo-init.sh also:

#!/bin/bash # # mongo -- "$MONGO_INITDB_DATABASE" <<EOF var rootUser = '$MONGO_INITDB_ROOT_USERNAME'; var rootPassword = '$MONGO_INITDB_ROOT_PASSWORD'; var user = '$MONGO_INITDB_USERNAME'; var passwd = '$MONGO_INITDB_PASSWORD'; var admin = db.getSiblingDB('admin'); admin.auth(rootUser, rootPassword); db.createUser({ user: user, pwd: passwd, roles: [ { role: "root", db: "admin" } ] }); EOF 

Dockerfile:

FROM mongo:3.6 COPY mongo-init.sh /docker-entrypoint-initdb.d/mongo-init.sh CMD [ "/docker-entrypoint-initdb.d/mongo-init.sh" ] 

docker-compose.yml:

version: '3' services: mongodb: build: . container_name: mongodb-test environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=example - MONGO_INITDB_USERNAME=myproject - MONGO_INITDB_PASSWORD=myproject - MONGO_INITDB_DATABASE=myproject myproject: image: myuser/myimage restart: on-failure container_name: myproject environment: - DB_URI=mongodb - DB_HOST=mongodb-test - DB_NAME=myproject - DB_USERNAME=myproject - DB_PASSWORD=myproject - DB_OPTIONS= - DB_PORT=27017 ports: - "80:80" 

After that, I went ahead and publish this Dockefile as an image to use in other projects.

note: without adding the CMD it mongo throws: unbound variable error

If you are using docker-compose and are trying to run any of these suggestions after you have created a volume, you may need to delete the volume because the init script will not run if the volume already there.

To see if this is the case, try running:-

docker volume ls

If your mongo data is there, you should see it in a volume names something like:-

<db>_<volume>

If the volume is something like db_volume, you will need to run:-

docker volume rm db_volume 

You may need to trash your container for this to work.

Steps to create multiple databases and users in MongoDB docker container without root access.

  1. Stop docker-compose with
    docker-compose down 
    command, or 'docker-compose -f docker-compose.yml -f docker-compose.dev.yml <...>' if you have multiple files.
  2. Remove volumes assigned to the mongo container. Run 'docker volume ls'. If there is anything similar to <projectfolder>_mongodb--vol, remove it by applying
    docker volume rm <projectfolder>_mongodb--vol 
  3. Set up folder with .js or .sh mongo database init scripts. For example, mongo-init/db_1.js:
    conn = new Mongo(); db = conn.getDB("db_1") db.createUser( { user: "db_1_service_user", pwd: "<password>", roles: [ { role: "readWrite", db: "db_1" } ] } ); 
    Repeat the procedure for mongo-init/db_2.js file. conn.getDB() creates the database, db.createUser() creates a user and assigns it to the database.
  4. docker-compose.yml fragment, notice the mount binding from ./mongo-init/ local folder containing database init scripts to the remote /docker-entrypoint-initdb.d/:
    version: "3.8" services: my-service_1: depends_on: - mongodb my-service_2: depends_on: - mongodb mongodb: image: mongo:4.4-bionic ports: - "27017:27017" volumes: - mongodb--vol:/data/db - ./mongo-init/:/docker-entrypoint-initdb.d/:ro healthcheck: test: "echo 'db.runCommand(\"ping\").ok'" interval: 5s timeout: 5s retries: 3 # in-service volumes are very slow in MacOS and Windows, # so using fast shared volumes for large storage volumes: mongodb--vol: 
  5. Apply connection strings to your applications:
    mongodb://db_1_service_user:<password>@mongodb:27017/db_1 
    mongodb://db_2_service_user:<password>@mongodb:27017/db_2 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy