A naive Dockerfile bakes your compilers, dev dependencies, and source into the final image — often 1GB+. Multi-stage builds let you compile in one stage and copy only the artifacts into a slim runtime stage.
A Multi-Stage Node Dockerfile
# --- Build stage ---
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# --- Runtime stage (slim) ---
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]Why It's Smaller and Safer
- Build tools and dev dependencies never reach production.
- A smaller image means faster pulls, faster deploys, and a smaller attack surface.
- Use
-slimor-alpinebase images and a.dockerignoreto shrink further.
Layer Caching
Copy package.json and install dependencies before copying source. Dependencies change rarely, so Docker reuses that layer and rebuilds in seconds.
