Getting started with Docker for local Node.js development

How to setup Docker-compose to isolate a web application

Apr 30, 2018 · 3 min read

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.

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

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
volumes:
- ./:/app
working_dir: /app
depends_on:
- mongo
environment:
NODE_ENV: development
ports:
- 3000:3000
command: npm run dev
mongo:
image: mongo
expose:
- 27017
volumes:
- ./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

François Romain

Written by

Freelance developer / designer → http://francoisromain.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade