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

NVIDIA-powered ML station with Fedora 29 + Docker

DevSecOps series No. 2 — Automatic checking Dockerfiles for security

What Is R, And How Is It Used?

Shroom Community Update #3

Begin with Web API in .net

Cloud Compute to Cloud Database Connectivity in Oracle Cloud Infrastructure

CLOUD MIGRATION & KEY CONSIDERATIONS

How to migrate kubernetes cluster from Docker to CRI-O container runtime

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

OAuth 2.0 with Passport.js Part — 2

Prisma: ORMs hightlight in Node.js

Creating a HTTP Server with NodeJS and logging requests with promised fs

Introducing RabbitMQ: Your Reliable Message Broker!