How to create a DB for MongoDB container on start up?

MongodbDockerDocker Compose

Mongodb Problem Overview


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?

Mongodb Solutions


Solution 1 - Mongodb

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.

Solution 2 - Mongodb

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 docker.io/mongo
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.

Solution 3 - Mongodb

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.

Solution 4 - Mongodb

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.

Solution 5 - Mongodb

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.

Solution 6 - Mongodb

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:

Solution 7 - Mongodb

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 http://localhost:8111/:

enter image description here

Solution 8 - Mongodb

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...

https://docs.docker.com/engine/swarm/secrets/

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)

Solution 9 - Mongodb

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,

Solution 10 - Mongodb

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
# https://stackoverflow.com/a/53522699
# https://stackoverflow.com/a/37811764
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

Solution 11 - Mongodb

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.

Solution 12 - Mongodb

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
    

Solution 13 - Mongodb

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:

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionReynierPMView Question on Stackoverflow
Solution 1 - MongodbPaul WasilewskiView Answer on Stackoverflow
Solution 2 - MongodbMattView Answer on Stackoverflow
Solution 3 - Mongodbx-yuriView Answer on Stackoverflow
Solution 4 - MongodbMateusz StefekView Answer on Stackoverflow
Solution 5 - Mongodbuser2350644View Answer on Stackoverflow
Solution 6 - MongodbAlf EatonView Answer on Stackoverflow
Solution 7 - MongodbvladimirView Answer on Stackoverflow
Solution 8 - MongodbenglishPeteView Answer on Stackoverflow
Solution 9 - MongodbLaTouwneView Answer on Stackoverflow
Solution 10 - MongodbLucas Almeida CarottaView Answer on Stackoverflow
Solution 11 - Mongodbuser1545858View Answer on Stackoverflow
Solution 12 - MongodbArtur AView Answer on Stackoverflow
Solution 13 - MongodbPatrik H.View Answer on Stackoverflow