Docker for Node.js applications: images that are small, fast and safe
A 1.2GB Node image that runs as root and ignores SIGTERM is the default outcome. Every part of that is avoidable.
Fast, clean Docker images come from a small pinned base image, layer ordering that puts volatile files last, a strict .dockerignore, BuildKit cache mounts for package managers, no secrets in any layer, and an automated vulnerability scan in CI. Reproducibility comes from pinning versions and committing lockfiles.
| Base | Size | Best for |
|---|---|---|
| node:22 | ~1.1 GB | Local development only |
| node:22-slim | ~200 MB | Native modules, glibc compatibility |
| node:22-alpine | ~130 MB | Most production workloads |
| gcr.io/distroless/nodejs22 | ~110 MB | Hardened runtime, no shell |
| scratch | 0 | Static binaries only |
# syntax=docker/dockerfile:1
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run buildEvery layer is stored and inspectable. A token passed via ARG appears in the build history; a file copied in and deleted in a later RUN still exists in the earlier layer. Both are recoverable by anyone who can pull the image.
# Wrong — recoverable from image history
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc && npm ci
# Right — BuildKit secret mount, never written to a layer
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ciARG GIT_SHA
LABEL org.opencontainers.image.revision="${GIT_SHA}" \
org.opencontainers.image.source="https://github.com/acme/app" \
org.opencontainers.image.licenses="UNLICENSED"Combine commands that belong together, such as apt-get update and install with cleanup. Do not combine everything — you lose cache granularity and rebuild the world on any change.
Smaller means faster pulls and a reduced attack surface, but distroless images have no shell, which makes incident debugging harder. Choose deliberately for your operational maturity.
Monthly at minimum, and immediately for critical CVEs. Automate it with a dependency bot so it becomes a routine pull request rather than a project.
A software bill of materials: a machine-readable inventory of everything in your image. It is what lets you answer 'are we affected?' within minutes of the next widely-publicised vulnerability.
ROVQIX Engineering
Engineering team, ROVQIX
The ROVQIX engineering team builds and maintains web platforms, APIs and infrastructure for clients across SaaS, ecommerce and enterprise. These notes come out of real production work — deploys, incidents, migrations and audits.
ROVQIXdesigns and builds production web platforms — Next.js front ends, Node.js APIs and the infrastructure behind them. Tell us what you're building and we'll scope it with you.
A 1.2GB Node image that runs as root and ignores SIGTERM is the default outcome. Every part of that is avoidable.
Container security starts with four lines in a Dockerfile and a scan in CI. Most breaches involve failures at that level, not exotic escapes.
A pipeline that takes 25 minutes and fails randomly does not improve quality. It teaches the team to merge on red.
No spam. Just the occasional case study and craft breakdown.