Skip to content
Back to Deployment

Docker Multi-Stage Builds

Multi-stage Dockerfiles for small production images by separating build and runtime.

ContainersIntermediate

Why

Multi-stage builds use one Dockerfile with multiple FROM statements — build stage has all dev deps, final stage copies only built artifacts. Reduces image size 10-100x.

Docker is used by 59% of professional developers per the Stack Overflow 2024 survey, and Kubernetes runs 60% of containerized workloads in production (CNCF 2024 survey). Container orchestration reduces deployment-related incidents by 35% through declarative rollouts.

When

Use multi-stage for all production images — smaller, more secure, faster to pull. Skip containers for simple static sites — a CDN + static export is cheaper and faster.

Avoid Kubernetes for teams under 10 people; the operational overhead is not justified.

How

1. Build stage — FROM node:20 AS build, full deps 2. Build — RUN npm ci && npm run build 3. Runtime stage — FROM node:20-alpine, slim 4. Copy — COPY --from=build /app/dist ./dist 5. Deps — COPY --from=build /app/node_modules ./ 6. CMD — run the built app 7. Verify — docker images, compare sizes

Example

# Dockerfile — multi-stage FROM node:20 AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY --from=build /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/server.js"] # Build and check size docker build -t myapp . docker images myapp # ~150MB vs ~1GB single-stage

Common Problems

  • ⚠Wrong stage copied — COPY --from=stage-name, ensure name matches AS clause
  • ⚠Dev deps in final — RUN npm ci --omit=dev in final stage, not full install
  • ⚠Configuration drift between environments — use infrastructure-as-code (Terraform/Pulumi) to keep dev/staging/prod identical

Best Practices

  • ✓Use alpine or distroless for final stage — smallest base, fewest vulnerabilities
  • ✓Copy only needed files — don't COPY . . in final, copy specific dist/ or build/
  • ✓Set up health checks and graceful shutdown — handle SIGTERM to drain in-flight requests before exit

FAQ