Categories
automation javascript Server

Node + MongoDB development environment w/Docker Compose

I’ve recently done some work on a personal project that I have put off for far too long. The project itself is a chat bot for moderating Telegram chat groups but this post isn’t about that project. Instead, it will focus on how I simplified setup of the development environment using Docker Compose.

I don’t have much experience with Docker or Docker Compose because in the past, I’ve used Vagrant for creating my environments. And Vagrant has been great, but over the years, I’ve run into a few issues:

  • virtual machines can take up to a minute to start, sometimes longer
  • new boxes (images) are slow to download
  • software versions fall out of sync with production environments
  • unexpected power outages have led to corrupted VMs

These issues are likely addressable in each configuration but between custom shell scripts and having so little experience with Ruby (Vagrantfiles are written in Ruby), managing it can quickly become unwieldy. Plus, Docker has become a de facto industry standard so it’s about time I learned something about it.

Ideally, this project would have a single file that makes spinning up a consistent development environment quick and painless, which is where Docker Compose comes in. A docker-compose.yml file; located in the root directory of the project, will make starting the environment as simple as running a single command; docker-compose up -d. The “-d” flag runs the containers in detached mode (the background) which frees up your terminal.

MongoDB

My first goal was to get MongoDB working with the project. This was relatively easy since the docker community maintains an official MongoDB image with an example docker-compose.yml:

# Use root/example as user/password credentials
version: '3.1'

services:

  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: example
      ME_CONFIG_MONGODB_URL: mongodb://root:[email protected]:27017/

Since I wanted to start simple, I ran Node locally and connected to the MongoDB container as if it were another machine. To do that, ports needed to be exposed to the host machine. I also needed a database setup and for that same database to persist. I decided to keep the Mongo Express container as it creates a useful web interface for checking data.

version: '3.1'
services:
  mongo:
    container_name: toximongo
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: toxipass
      MONGO_INITDB_DATABASE: toxichatdb
    ports:
      - 27017:27017
    volumes:
        - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
        - ./mongo-volume:/data/db
  mongo-express:
    container_name: toximongo-express
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: toxipass
      ME_CONFIG_MONGODB_URL: mongodb://root:[email protected]:27017/toxichatdb

Some of the changes to take note of:

  • name containers to make them easy to differentiate and access
  • usernames + passwords set
  • expose MongoDB to local machine on port 27017
  • create a persistent volume at ./mongo-volume/ in the root of the project
  • run /docker-entrypoint-initdb.d/init-mongo.js to create a user and initialize the database

init-mongo.js is a very simple script that runs when the container is started for the first time.

db.createUser({
  user: 'root',
  pwd: 'toxipass',
  roles: [
    {
      role: 'readWrite',
      db: 'toxichatdb',
    },
  ],
});

Node

After getting access to a database, the next step was to include Node. Since Node versions change frequently, its great to ensure that all developers are supporting the same version. It also simplifies getting started working on the project if a developer isn’t expected to have to install something else. This was also straightforward since there is an official image supplied by the Node JS Docker team with extensive documentation.

version: '3.1'

services:
  node:
    container_name: toxinode
    image: "node:16"
    user: "node"
    working_dir: /home/node/app
    environment:
        - NODE_ENV=development
    volumes:
        - ./:/home/node/app
    expose:
        - "8080"
    command: [sh, -c, "yarn && yarn start"]

  mongo:
    container_name: toximongo
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: toxipass
      MONGO_INITDB_DATABASE: toxichatdb
    ports:
      - 27017:27017
    volumes:
        - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
        - ./mongo-volume:/data/db

  mongo-express:
    container_name: toximongo-express
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: toxipass
      ME_CONFIG_MONGODB_URL: mongodb://root:[email protected]:27017/toxichatdb

Again, I’ve given the container a custom name. There isn’t much else changed except for the command which issues a shell inside the container, calls Yarn to install dependencies, and then starts the application.

Summary

Since incorporating Docker Compose with this project, I’ve been impressed at the speed with which my development environment can start. This setup doesn’t include a Dockerfile but because it isn’t building custom images, I didn’t think that was necessary to include. I’m certain there are ways to improve it and I’ve got lots to learn, especially if I’d like to incorporate deploying this to production environments. If you’ve got tips or suggestions, let me know!

By Dylan Hildenbrand

10+ years experience in web development, homelab enthusiast, tinkerer

profile for Dylan Hildenbrand on Stack Exchange, a network of free, community-driven Q&A sites

Do you like these posts? Consider donating!

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.