Day 36: BuildKit and multi-stage builds
BuildKit: Docker's modern build engine
BuildKit builds layers in parallel where possible, skips stages whose output isn't needed, and caches more intelligently than the legacy builder — enabled by default in modern Docker.
Multi-stage builds
The problem: building a Node/TypeScript app needs devDependencies, a compiler, and source files — none of which should ship in the production image. A multi-stage build uses one stage to build the app, then copies only the compiled output into a fresh, minimal final stage — the build tools never make it into the shipped image.
# Stage 1: build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: run — only the compiled output ships
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json .
CMD ["node", "dist/index.js"]Layer ordering is a caching strategy
Copying package*.json and running npm ci *before* copying the rest of the source means Docker can reuse the cached dependency-install layer whenever only application code changes — dependencies rarely change, source code changes constantly.
Key terms
- BuildKit
- Docker's modern build engine — parallel builds, smarter caching, skips unused stages.
- Multi-stage build
- A Dockerfile with multiple FROM stages, where only needed artifacts are copied into the final, minimal stage.
Why does a multi-stage build reduce the final image size compared to a single-stage build?