Bootstrap container using node command instead of npm

One paragraph explainer

We are used to see code examples where folks start their app using CMD 'npm start'. This is a bad practice. The npm binary will not forward signals to your app which prevents graceful shutdown (see [/sections/docker/graceful-shutdown.md]). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. npm start also results in having an extra process for no benefit. To start you app use CMD ['node','server.js']. If your app spawns child-processes also use TINI as an entrypoint.

Code example - Bootsraping using Node

  1. FROM node:12-slim AS build
  2. WORKDIR /usr/src/app
  3. COPY package.json package-lock.json ./
  4. RUN npm ci --production && npm clean cache --force
  5. CMD ["node", "server.js"]

Code example - Using Tiny as entrypoint

  1. FROM node:12-slim AS build
  2. # Add Tini if using child-processes
  3. ENV TINI_VERSION v0.19.0
  4. ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
  5. RUN chmod +x /tini
  6. WORKDIR /usr/src/app
  7. COPY package.json package-lock.json ./
  8. RUN npm ci --production && npm clean cache --force
  9. ENTRYPOINT ["/tini", "--"]
  10. CMD ["node", "server.js"]

Antipatterns

Using npm start

  1. FROM node:12-slim AS build
  2. WORKDIR /usr/src/app
  3. COPY package.json package-lock.json ./
  4. RUN npm ci --production && npm clean cache --force
  5. # don’t do that!
  6. CMD "npm start"

Using node in a single string will start a bash/ash shell process to execute your command. That is almost the same as using npm

  1. FROM node:12-slim AS build
  2. WORKDIR /usr/src/app
  3. COPY package.json package-lock.json ./
  4. RUN npm ci --production && npm clean cache --force
  5. # don’t do that, it will start bash
  6. CMD "node server.js"

Starting with npm, here’s the process tree:

  1. $ ps falx
  2. UID PID PPID COMMAND
  3. 0 1 0 npm
  4. 0 16 1 sh -c node server.js
  5. 0 17 16 \_ node server.js

There is no advantage to those two extra process.

Sources:

https://maximorlov.com/process-signals-inside-docker-containers/

https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#handling-kernel-signals