Docker Cheatsheet

Multi-stage Builds

Use this Docker reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Why Multi-stage Builds

One Dockerfile, multiple FROM instructions. Earlier stages compile/test; only the final stage ships. Result: production images contain no compilers, test deps, or source code — often 10x smaller.

Basic Pattern

# Stage 1 — build
FROM node:22 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2 — production
FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/server.js"]

Copying Between Stages

# Copy a single file
COPY --from=builder /app/bin/myapp /usr/local/bin/myapp

# Copy a directory
COPY --from=builder /app/public ./public

# Copy from a public image (not just earlier stages)
COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/nginx/nginx.conf

# Copy from an absolute stage index (0-based)
COPY --from=0 /app/dist ./dist

Go Binary Example (Scratch Base)

FROM golang:1.23 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/server

FROM scratch
COPY --from=builder /app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/app"]

Python Example

FROM python:3.13 AS deps
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt

FROM python:3.13-slim AS runtime
COPY --from=deps /install /usr/local
WORKDIR /app
COPY . .
USER nobody
CMD ["python", "main.py"]

Test Stage Pattern

FROM node:22 AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM deps AS test
COPY . .
RUN npm test           # build fails here if tests fail

FROM deps AS build
COPY . .
RUN npm run build

FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

Targeting a Specific Stage

# Build only up to the "test" stage
docker build --target test -t myapp:test .

# Build the final (default) stage
docker build -t myapp:latest .

# Dev image stops at a "dev" stage that includes devDependencies
docker build --target dev -t myapp:dev .

Cache Mounts in Build Stages

# syntax=docker/dockerfile:1
FROM golang:1.23 AS builder
WORKDIR /src
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
    --mount=type=cache,target=/go/pkg/mod \
    go build -o /app .

Stage Naming Conventions

NamePurpose
baseShared base (OS + common tools)
deps / vendorDependency install only
builder / buildCompile / transpile
testRun tests (fails build if broken)
devDev tooling, hot reload
runtime / prodFinal shipping image

Size Comparison Cheat Sheet

ApproachTypical size
Single-stage Node.js (node:22)~1.1 GB
Multi-stage → node:22-alpine~200 MB
Go: multi-stage → scratch5–15 MB
Python: multi-stage → slim~80 MB