Getting started with Docker for local Node.js development

How to setup Docker-compose to isolate a web application

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.

  • Share content between the host and the container to avoid restarting the container after each modification.

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"
}
}
  • start: uses node for production.

Docker

Docker runs any process in isolation in their own container.

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

Create Docker containers

Let’s create two containers with docker-compose:

  • One for MongoDB
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
  • 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

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

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

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