Dockerfile good practices for Node and NPM

Layers of containers, that will make sense later.

NodeJS and NPM examples

Laverage non-root user

# Copy files as a non-root user. The `node` user is built in the Node image.
WORKDIR /usr/src/app
RUN chown node:node ./
USER node

Set NODE_ENV=production by default

# Defaults to production, docker-compose overrides this to development on build and run.
ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV
version: '3'
services:
myapp:
build:
args:
- NODE_ENV=development
context: ./
environment:
- NODE_ENV=development

Install NPM dependencies before adding code

# Install dependencies first, as they change less often than code.
COPY package.json package-lock.json* ./
RUN npm ci && npm cache clean --force
COPY ./src ./src
$ docker build .
Sending build context to Docker daemon
Step 2/5 : COPY package.json package-lock.json* ./
---> Using cache
---> 6fb28308975d
Step 3/5 : RUN npm ci && npm cache clean --force
---> Using cache
---> 0a6bd71d2c2d

Use node (not NPM) to start the server

# Execute NodeJS (not NPM script) to handle SIGTERM and SIGINT signals.
CMD ["node", "./src/index.js"]
const http = require('http');
const port = process.env.PORT || 8000;
http.createServer(function (req, res) {
res.end(req.url);
}).listen(port);
console.log(`Server running at http://localhost:${port}/ ...`);
// Signal handling
process.on('SIGTERM', function() {
console.log('SIGTERM: shutting down...');
});

Builder pattern

FROM rubygem/compass AS builder
COPY ./src/public /dist
WORKDIR /dist
RUN compass compile
# Output: css/app.css
# Copy compiled CSS styles from builder image.
COPY --from=builder /dist/css ./dist/css

Putting it all together

# Separate builder stage to compile SASS, so we can copy just the resulting CSS files.
FROM rubygem/compass AS builder
COPY ./src/public /dist
WORKDIR /dist
RUN compass compile
# Output: css/app.css
# Use NodeJS server for the app.
FROM node:12
# Copy files as a non-root user. The `node` user is built in the Node image.
WORKDIR /usr/src/app
RUN chown node:node ./
USER node
# Defaults to production, docker-compose overrides this to development on build and run.
ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV
# Install dependencies first, as they change less often than code.
COPY package.json package-lock.json* ./
RUN npm ci && npm cache clean --force
COPY ./src ./src
# Copy compiled CSS styles from builder image.
COPY --from=builder /dist/css ./dist/css
# Execute NodeJS (not NPM script) to handle SIGTERM and SIGINT signals.
CMD ["node", "./src/index.js"]

--

--

--

Father. Husband. Solutions developer profesionally (software quite often). Arsenal supporter. Cyclist.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Waterbear Cloud Newsletter, September 2019

5 Things I Learned Organizing Django Girls Aba.

AWS Lightsail PHP updating is hard

Find and Replace in a CSV using Python

How to install GEOS for PHP 8.1 on an Apple Silicon Mac

Communicating through UUID conflicts

Rails RESTful Routing Convention Decision: To Keep Or To Break?

Uninstall odoo from Linux / Debian

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Adam Brodziak

Adam Brodziak

Father. Husband. Solutions developer profesionally (software quite often). Arsenal supporter. Cyclist.

More from Medium

Dockerizing a node.js application

Install MongoDB in Ubuntu with Authentication

Reactive JQuery using Vuex design pattern

How To Use CORS in NestJS Application