Getting started with Docker for local Node.js development

How to setup Docker-compose to isolate a web application

François Romain
3 min readApr 30, 2018

Each computer has a specific environment: some tools and specific version of languages are globally installed, our applications share databases… When we work on a web project, the production server might be different from our computer. This is error prone.

Docker solves this problem by running applications in isolated environments.

In this article, we are going to

  • Take an existing Node.js / MongoDB application and make it work inside Docker containers.
  • Share content between the host and the container to avoid restarting the container after each modification.

- install Docker on your computer.

The application

As an example, we’ll take a very simple Node.js / Express / MongoDB aplication. Here is the package.json of this app:

"name": "test-app",
"version": "0.1.0",
"main": "index.js",
"scripts": {
"dev": "nodemon ./index.js",
"start": "node ./index.js"
"dependencies": {
"express": "^4.16.3",
"mongoose": "^5.0.16"
"devDependencies": {
"nodemon": "^1.17.3"

It has two scripts:

  • dev: uses nodemon for development,
  • start: uses node for production.

Right now we start the dev server with npm run dev. Express is configured to listen to incoming requests at https://localhost:3000.

We need to have MongoDB installed globally.

So, let’s use Docker for a better isolation.


Docker runs any process in isolation in their own container.

The advantages of Docker are:
- Apps always run in the same environment (local computer, live server).
- Apps are sandboxed to keep them separate and avoid potential conflicts.
- Apps are easy to share with all their dependencies.

The scope of Docker tools is really large including:

  • Docker (docker-cli) and the related Dockerfiles to run single containers.
  • Docker-compose cli and the related docker-compose.yml files to run services (multi-containers application).

Docker-compose can be used together with dockerfiles, but not necessary. Also, the scopes of Docker and Docker-compose often overlap but use different syntaxes…

Create Docker containers

Let’s create two containers with docker-compose:

  • One for the Node.js app
  • One for MongoDB

At the root of the project, create a docker-compose.yml file with the following content:

version: "3"services:  app:
image: node:alpine
- ./:/app
working_dir: /app
- mongo
NODE_ENV: development
- 3000:3000
command: npm run dev
image: mongo
- 27017
- ./data/db:/data/db

This files creates two services: app (the Node.js application) and mongo (the database):

  • image: a set of instructions to build a container with a specific environment. Here we use the node:alpine and mongo images which are downloaded from the Docker hub.
  • volumes: share content between the local host and the container. This is handy for development to reflects local changes without having to restart the container. I wouldn’t do that in production. Instead, I would copy the content from the host to the container for a better isolation.
  • working_dir: the directory where the command instruction is executed.
  • depends_on: defines dependency between services.
  • environment: env variables.
  • ports: share ports between the host and the container. Here the port 3000 on the host is configured to point to the port 3000 in the container.
  • command: an instruction that start the application. Here, this is the dev script from the package.json file.
  • expose: expose a port from the container to a docker network. Here, we don’t declare an explicit network, so our two services are bound the default network. This means that inside the Node app, mongo is referenced with mongoose.connect('mogodb://mongo:27017'). Unlike the ports instruction, this does not make the port available on the host.

Start the services

In the terminal:

docker-compose up

Now, the app runs in isolated containers with their own version of Node.js and MongoDB. Also, mongo is only available in the docker-network. The app still listens at http://localhost:3000.

Useful commands

Hide logs

Start the container in detached mode:

docker-compose up -d

List all running containers

docker ps -a

Start a shell in one container

Get the name of the container from the command above, then:

docker exec -ti <container-name> /bin/bash