Build NodeJS project with Docker

Kien Duong
3 min readJun 22, 2023

My blog: https://ai-research.dev/build-nodejs-project-with-docker/

First of all, you must have an overview about Docker Compose at https://docs.docker.com/compose/

In the root project, we must create the docker-compose.yml file to define the necessary services for a NodeJS project.

Inside the package.json file, we define 3 new commands for running Docker:

// package.json
{
...
"scripts": {
...
"docker:up": "docker-compose up",
"docker:down": "docker-compose down",
"docker:restart": "docker-compose restart",
...
},
...
}

1. MySQL service

Documentation: https://hub.docker.com/_/mysql

Create a new Dockerfile for MySQL service at docker/services/mysql/ folder.

In the .env file, we also need to define some variables that will be used in the Dockerfile.

  • DOCKER_APP_NAME
  • DOCKER_MYSQL_PORT
  • DOCKER_MYSQL_PORTS
  • DOCKER_MYSQL_DB_NAME
  • DOCKER_MYSQL_ROOT_PASSWORD
  • DOCKER_MYSQL_PASSWORD
  • DOCKER_MYSQL_PORT
// Dockerfile
FROM mysql:8
// .env
# Docker
DOCKER_APP_NAME=nodejs-sample
DOCKER_MYSQL_PORT=3307
DOCKER_MYSQL_PORTS=3307:3307
DOCKER_MYSQL_DB_NAME=nodejs-sample
DOCKER_MYSQL_ROOT_PASSWORD=root
DOCKER_MYSQL_USER=root
DOCKER_MYSQL_PASSWORD=root
// docker-compose.yml
...
mysql:
build:
context: .
dockerfile: ./docker/services/mysql/Dockerfile
restart: always
container_name: ${DOCKER_APP_NAME}_mysql
networks:
- net
expose:
- ${DOCKER_MYSQL_PORT}
ports:
- "${DOCKER_MYSQL_PORTS}"
volumes:
- dbdata:/var/lib/mysql/
environment:
- MYSQL_DATABASE=${DOCKER_MYSQL_DB_NAME}
- MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- MYSQL_TCP_PORT=${DOCKER_MYSQL_PORT}
- MYSQL_ROOT_HOST=%
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-authentication-plugin=mysql_native_password'
]

2. Node service

Documentation: https://hub.docker.com/_/node

Add more some variables in the .env file:

  • DOCKER_APP_PATH
  • DOCKER_NODE_PORTS

Create a new Dockerfile for Node service in the docker/services/node folder.

// .env
DOCKER_APP_PATH=/var/www/nodejs-sample
DOCKER_NODE_PORTS=5555:5555
// Dockerfile
FROM node:14.14.0-alpine

RUN apk update && apk upgrade && apk add --no-cache \
bash \
curl \
gettext && \
npm install -g nodemon && \
npm install -g pm2 && \
npm rebuild bcrypt --build-from-source
// docker-compose.yml
node:
restart: on-failure
depends_on:
- mysql
build:
context: .
dockerfile: ./docker/services/node/Dockerfile
container_name: ${DOCKER_APP_NAME}_node
working_dir: ${DOCKER_APP_PATH}
networks:
- net
ports:
- "${DOCKER_NODE_PORTS}"
entrypoint: sh /bin/node/command.sh
volumes:
- .:${DOCKER_APP_PATH}
- ${DOCKER_APP_PATH}/node_modules
- ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh
environment:
- DB_HOST=${DOCKER_APP_NAME}_mysql
- DB_USER=${DOCKER_MYSQL_USER}
- DB_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- DB_NAME=${DOCKER_MYSQL_DB_NAME}
- DB_PORT=${DOCKER_MYSQL_PORT}
- NODE_ENV=${NODE_ENV}
- PORT=${PORT}
- TOKEN_SECRET_KEY=${TOKEN_SECRET_KEY}

As you can see, we’ve added some new configs for node service

  • entrypoint: sh /bin/node/command.sh It means that the system will run command.sh file in the building process

Where is command.sh from? On the next lines, you can see the config: ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh

Depends on the running environment, the system will map the appropriate command file. In the docker/services/node folder, we will create two new files.

  • command-development.sh for development env.
  • command-production.sh for production env.

The difference between them is that while command-development.sh will run the application in the development environment with nodemon, command-production.sh will build & run application in the production environment with pm2-runtime.

// command-development.sh
#!/usr/bin/env bash

yarn install && yarn dev
// command-production.sh
#!/usr/bin/env bash

yarn install && yarn build && cd dist && pm2-runtime start index.js

The docker-compose.yml file for both mysql & node services:

// docker-compose.yml
version: '3'

services:
mysql:
build:
context: .
dockerfile: ./docker/services/mysql/Dockerfile
restart: always
container_name: ${DOCKER_APP_NAME}_mysql
networks:
- net
expose:
- ${DOCKER_MYSQL_PORT}
ports:
- "${DOCKER_MYSQL_PORTS}"
volumes:
- dbdata:/var/lib/mysql/
environment:
- MYSQL_DATABASE=${DOCKER_MYSQL_DB_NAME}
- MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- MYSQL_TCP_PORT=${DOCKER_MYSQL_PORT}
- MYSQL_ROOT_HOST=%
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-authentication-plugin=mysql_native_password'
]
node:
restart: on-failure
depends_on:
- mysql
build:
context: .
dockerfile: ./docker/services/node/Dockerfile
container_name: ${DOCKER_APP_NAME}_node
working_dir: ${DOCKER_APP_PATH}
networks:
- net
ports:
- "${DOCKER_NODE_PORTS}"
entrypoint: sh /bin/node/command.sh
volumes:
- .:${DOCKER_APP_PATH}
- ${DOCKER_APP_PATH}/node_modules
- ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh
environment:
- DB_HOST=${DOCKER_APP_NAME}_mysql
- DB_USER=${DOCKER_MYSQL_USER}
- DB_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- DB_NAME=${DOCKER_MYSQL_DB_NAME}
- DB_PORT=${DOCKER_MYSQL_PORT}
- NODE_ENV=${NODE_ENV}
- PORT=${PORT}
- TOKEN_SECRET_KEY=${TOKEN_SECRET_KEY}

networks:
net:
driver: bridge

volumes:
dbdata:
driver: local

Running the defined command yarn docker:up to start the Docker. The nodejs application has been built.

Your nodejs app has been working on the port 5555. Open a new terminal tab to see list containers by the command docker container ls

You can see that you’ve had two containers for node & mysql services. Node service has been running at port 5555 & mysql service has been running at port 3307.

Source code: https://github.com/duongduckien/nodejs-sample/tree/feature/docker

--

--