Booting a mongoDB container with user specified credentials

In this quick tutorial, we will be walking through the idea of creating MongoDB instances along with user supplied authentication credentials.

I've always wanted to have it like the official Postgres docker images.

$ docker run --name some-postgres -e POSTGRES_PASSWORD=Passw0rdXYZ -d postgres

Well guess what? The official Mongo containers have their equivalents, at least according to this github issue. But it isn't clear on how to go about using them.

My first attempt was to pass them along, like how we do it in Postgres.

$ docker run  \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  -e MONGO_INITDB_DATABASE=abcd \
  -p 27017 \
  mongo:3.6

No dice. I checked out the init script of the image, and it has this check,

        for f in /docker-entrypoint-initdb.d/*; do
            case "$f" in
                *.sh) echo "$0: running $f"; . "$f" ;;
                *.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;;
                *)    echo "$0: ignoring $f" ;;
            esac
            echo
        done

What this means is, if you supply the image with init scripts, it will run them against the $MONGO_INITDB_DATABASE database. Good enough, but not quite the developer experience I prefer :)

Taking a quick detour to show how MongoDB authentication works. We first create administrative users in the admin database. This step happens just before running the init scripts,

        if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
            rootAuthDatabase='admin'

            "${mongo[@]}" "$rootAuthDatabase" <<-EOJS
                db.createUser({
                    user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"),
                    pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"),
                    roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ]
                })
            EOJS
        fi

There is no separate database creation step in MongoDB unlike its SQL cousins. We simply specify it as part of connection string.

Let's try and bake in the init script along with the image. A quick init script in the same directory as the Dockerfile.

db.createUser(
   {
     user: "mycustomuser",
     pwd: "Pa$$w0rd123",
     roles: [ "readWrite", "dbAdmin" ]
   }
)

And add the script to the correct path,

COPY docker-entrypoint.sh /usr/local/bin/

COPY seed-data.js /docker-entrypoint-initdb.d/

ENTRYPOINT ["docker-entrypoint.sh"]

I was able to use the above credentials in the $MONGO_INITDB_DATABASE database. Again, good enough, but still not as good as specifying them as env variables.

I discarded this approach and added this in the init script after the admin user creation step.

        if [ "$MONGO_USERNAME" ] && [ "$MONGO_PASSWORD" ]; then
                "${mongo[@]}" "$MONGO_INITDB_DATABASE" <<-EOJS
                db.createUser({
                    user: $(_js_escape "$MONGO_USERNAME"),
                    pwd: $(_js_escape "$MONGO_PASSWORD"),
                    roles: [ "readWrite", "dbAdmin" ]
                })
            EOJS
        fi

I added this in place of the script running part. You can still have it around if you want to import some initial data into MongoDB.

Now, take 2.

$ docker run  \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
-e MONGO_INITDB_DATABASE=abcd \
-e MONGO_USERNAME=foo \
-e MONGO_PASSWORD=barr123 \
-p 27017 \
lakshminp/mongo:1.3

Works just as I imagined!

You can grab the modified init script here.