Writing a Production-Ready Dockerfile for Node.js

A
Author
13 Sep, 2026 1 min read 707 views

A Dockerfile that "works" and one that's good for production differ in image size, build speed, security and how the app shuts down. Here's a solid template.

# syntax=docker/dockerfile:1

# ---------- dependencies (all, for building) ----------
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

# ---------- build ----------
FROM deps AS build
COPY . .
RUN npm run build && npm prune --omit=dev

# ---------- runtime ----------
FROM node:22-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app

COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
COPY --chown=node:node package.json ./

USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/healthz || exit 1
CMD ["node", "dist/server.js"]

Why each part matters

  • Multi-stage build: compilers, dev dependencies and source files stay in earlier stages; the final image contains only what runs.
  • Copy the lockfile first: the slow npm ci layer is cached until dependencies change, so code-only changes rebuild in seconds.
  • npm ci installs exactly what the lockfile says, failing if it's out of sync.
  • Cache mount keeps npm's download cache between builds without putting it in the image.
  • USER node: the official image ships a non-root user; never run your app as root.
  • Exec-form CMD (JSON array) runs node as PID 1, so it receives SIGTERM directly and can shut down gracefully. npm start would swallow the signal.

Don't forget .dockerignore

node_modules
dist
.git
.env*
*.log
coverage

Without it, COPY . . sends your local node_modules and secrets into the build context.

Pin versions

Use a specific major version tag (node:22-alpine), not latest. For fully reproducible builds, pin the image digest too (node:22-alpine@sha256:...) and update it deliberately.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.