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

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.


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.


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.