Multi-stage Builds
Build and compile in one stage, copy only necessary artifacts to the final image.
Basic Pattern
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]Go Application
Go compiles to a single binary, perfect for minimal images.
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# Production stage - scratch (no OS)
FROM scratch
COPY --from=builder /app/main /main
ENTRYPOINT ["/main"]Node.js with TypeScript
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
RUN npm prune --production
# Production stage
FROM node:20-alpine
WORKDIR /app
# Copy only production dependencies and built code
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]Python Application
# Build stage - install and compile
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
# Production stage
FROM python:3.12-slim
WORKDIR /app
# Copy wheels and install
COPY --from=builder /wheels /wheels
RUN pip install --no-cache /wheels/*
COPY . .
USER nobody
EXPOSE 8000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]React Frontend
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage - Nginx
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Named Stages
# Base stage with common dependencies
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
# Development stage
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
# Test stage
FROM base AS test
RUN npm ci
COPY . .
CMD ["npm", "test"]
# Build stage
FROM base AS builder
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]# Build specific stage
docker build --target development -t myapp:dev .
docker build --target test -t myapp:test .
docker build --target production -t myapp:prod .Size Comparison
| Stage | Base Image | Typical Size |
|---|---|---|
| Builder | node:20 | 1.1 GB |
| Production | node:20-alpine | 150 MB |
| Go + scratch | scratch | 10-20 MB |
| Distroless | gcr.io/distroless/static | 2-5 MB |
Benefits
- Smaller images - Only runtime dependencies
- Better security - No build tools in production
- Faster deployment - Smaller images transfer faster
- Cleaner separation - Build vs runtime concerns
- dockerfile
- multi-stage
- build
- optimization
- production