| Concept | Description |
|---------|-------------|
−| Build stage | A named \`FROM\` block that produces intermediate artifacts; only the final stage ships |
+| Build stage | A named `FROM` block that produces intermediate artifacts; only the final stage ships |
| Layer cache | Docker caches each instruction; reordering instructions keeps cache hits high |
−| .dockerignore | Excludes files from the build context to speed up \`COPY\` and reduce image size |
+| .dockerignore | Excludes files from the build context to speed up `COPY` and reduce image size |
| Distroless / slim | Minimal base images with no shell or package manager — smaller, fewer CVEs |
+| BuildKit | Modern Docker build engine with parallel stage execution and improved caching, cache mounts, and build-time secrets |
−| BuildKit | Modern Docker build engine with parallel stage execution and better caching |
+| Dockerfile frontend (syntax) | BuildKit supports external Dockerfile frontends. Add a syntax directive (for example `# syntax=docker/dockerfile:1`) to pin or select the frontend image. Use the stable `docker/dockerfile:1` channel for feature updates or pin a specific immutable tag for reproducible builds (see Docker Docs). |
| HEALTHCHECK | Built-in instruction that lets Docker monitor container health |
| Multi-platform | Building for linux/amd64 and linux/arm64 from a single Dockerfile |
+
+Notes and sources:
+- Always use a syntax directive when relying on BuildKit-specific features (Docker Docs: Custom Dockerfile syntax).
+- Prefer stable channel `docker/dockerfile:1` for small compatibility updates, or pin a precise immutable tag when strict reproducibility is required.
## Workflow
@@ −40 +45 @@
| Base image | Compressed size | Use when |
|------------|----------------|----------|
+| `node:22-alpine` | ~50 MB | Default for most Node.js services |
+| `node:22-slim` | ~75 MB | Need glibc or native deps that fail on musl |
+| `gcr.io/distroless/nodejs22` | ~40 MB | Maximum security — no shell, no package manager |
−| \`node:22-alpine\` | ~50 MB | Default for most Node.js services |
+| `scratch` | 0 MB | Fully static binaries only |
−| \`node:22-slim\` | ~75 MB | Need glibc or native deps that fail on musl |
−| \`gcr.io/distroless/nodejs22\` | ~40 MB | Maximum security — no shell, no package manager |
−| \`scratch\` | 0 MB | Fully static binaries only |
### Step 2: Structure the multi-stage build
@@ −59 +64 @@
3. Copy lockfile → install deps
4. Copy source → build
5. Copy build output into final stage
+
+### Step 4: Use BuildKit features to speed, secure, and shrink builds
+
+- Add an explicit syntax directive at the top of Dockerfiles that use BuildKit-specific features: `# syntax=docker/dockerfile:1`.
+- Use cache mounts to persist package-manager caches between builds and avoid re-downloading dependencies (`RUN --mount=type=cache,...`). See Docker Docs: Optimize cache usage in builds.
+- Use secret mounts for build-time secrets instead of ARGs or ENV so secrets are not baked into layers (`RUN --mount=type=secret,id=npmrc ...`). See Docker Docs: Build secrets.
+- Use SSH mounts for fetching private repositories during the build (`RUN --mount=type=ssh ...`).
−### Step 4: Add security hardening
+### Step 5: Add security hardening
- Run as a non-root user
−- Drop all Linux capabilities
+- Drop Linux capabilities where supported by your runtime/orchestrator
- Set read-only filesystem where possible
- Pin base image digests for reproducibility
−### Step 5: Write a comprehensive .dockerignore
+### Step 6: CI and reproducible builds
+- Use `docker buildx` and a shared external cache (registry or GitHub Actions cache) to get repeatable, fast builds across runners
+- Pin Dockerfile frontend and build tooling versions in CI
−Exclude everything that is not needed in the build context.
+- Pin scanner and action versions; do not use `@master` or `@latest` for security-sensitive tools
## Examples
−### Example 1: Production Next.js multi-stage Dockerfile
+### Example 1: Production Next.js multi-stage Dockerfile (BuildKit primitives)
−\`\`\`dockerfile
+```dockerfile
# syntax=docker/dockerfile:1
# ---- Stage 1: Dependencies ----
FROM node:22-alpine AS deps
WORKDIR /app
+# Cache package manager files between builds (BuildKit cache mount)
COPY package.json pnpm-lock.yaml ./
+RUN --mount=type=cache,id=pnpm-cache,target=/root/.pnpm-store \
+ corepack enable pnpm && pnpm install --frozen-lockfile --prod=false
+
+# If you need private registries, use build secrets (will not be in final layers)
+# docker build --secret id=npmrc,src=$HOME/.npmrc .
+# and in Dockerfile:
+# RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \