Docker cookbook · Beginner
Build a production image for a Node.js service
Package a Node.js or TypeScript service with only production dependencies and a non-root user.
Approach
Build in one stage (with dev dependencies for TypeScript), then copy the compiled output and production
dependencies into a clean runtime stage that runs as the node user.
Solution
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:24-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Pitfalls
npm installinstead ofnpm ci, giving non-reproducible dependency trees.- Copying host
node_modulesinto the image (add it to.dockerignore). - Starting with
npm start, which adds a process that may not forward signals.
Variations
- With pnpm, enable Corepack and use
pnpm install --frozen-lockfileandpnpm deploy --prod. - Bundle the app (esbuild) to ship a single file and skip node_modules entirely.
Further reading: Node.js Docker best practices
Reference: https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md
More Docker recipes
- Add a healthcheck to an HTTP service
- Build a layered image for a Spring Boot app
- Build a production image for a Python web app
- Build, cache and push images in GitHub Actions
- Build one image tag for amd64 and arm64
- Configure nginx from environment variables
- Develop with Compose and live reload
- Make containers cope with dependencies that start slowly