Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Authors small, secure, reproducible multi-stage Dockerfiles with build-cache optimization, pinned base images, non-root runtime users, and minimal attack surface. Use this skill when writing, reviewing, hardening, or shrinking a Dockerfile or container image — e.g. "write a Dockerfile", "containerize this app", "my image is too big", "make this container secure / non-root", "optimize Docker build cache", "multi-stage build", "reduce image layers", "fix Docker best practices", or "review my Docke
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 99% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 88% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 67% | 0% |
| case-08 | ✓→✓ | = Same ✓ | 355% | 0% |
Produce production-grade Dockerfiles that are small (minimal final image), secure (non-root, pinned, no secrets, minimal surface), and reproducible (deterministic builds with effective layer caching). Applies to any language runtime (Go, Rust, Node, Python, Java, .NET, etc.) and any registry.
Keywords: Dockerfile, container, multi-stage build, build cache, BuildKit, non-root user, distroless, alpine, slim, image size, layer caching, .dockerignore, HEALTHCHECK, OCI image, supply chain, reproducible build, hadolint, Trivy.
The three goals (small / secure / reproducible) often align, but when they conflict, prioritize security > reproducibility > size.
Follow these steps in order when authoring or reviewing a Dockerfile.
FROM scratch or distroless final stage. Interpreted/runtime (Node/Python/Java/.NET) → use the matching -slim/distroless runtime image. JVM/CLR may need a JRE-only final image.builder stage installs toolchains and compiles/installs dependencies; the final stage copies only the runtime artifacts. Never ship compilers, package caches, or dev headers.image:tag@sha256:...). Pin OS and language package versions. This is what makes builds reproducible.package.json, go.mod, requirements.txt, *.csproj) and install deps before copying application source. Source changes then don't bust the dependency layer.RUN --mount=type=cache,...) for package-manager caches so repeated builds are fast without bloating the image.RUN steps, clean package caches in the same layer, and copy with --chown instead of a separate chown layer.WORKDIR, EXPOSE, ENV, OCI LABELs, HEALTHCHECK, and an exec-form ENTRYPOINT/CMD..dockerignore. Exclude .git, node_modules, build output, secrets, and CI files so the build context stays small and secrets never enter an image.hadolint Dockerfile and a vulnerability scan (e.g. trivy image). See scripts/check_dockerfile.py for a fast static audit you can run with zero dependencies.| App type | Recommended final base | Notes | |---|---|---| | Static Go / Rust binary | gcr.io/distroless/static or scratch | Add CA certs + /etc/passwd if using scratch. | | Dynamically-linked binary | gcr.io/distroless/base or *-slim | Needs libc. | | Node.js | gcr.io/distroless/nodejs22-debian12 or node:22-slim | Distroless has no shell — great for prod, harder to debug. | | Python | python:3.12-slim or gcr.io/distroless/python3 | Prefer slim + venv copy. | | Java | eclipse-temurin:21-jre or gcr.io/distroless/java21 | JRE only, never the JDK, in the final stage. | | .NET | mcr.microsoft.com/dotnet/aspnet:8.0 (or -chiseled) | chiseled images are distroless-style and non-root by default. | | Needs a shell/debug | *-slim or *-alpine | Alpine uses musl — watch for glibc-specific bugs. |
Rule of thumb: distroless or scratch for compiled apps; -slim for interpreted apps. Avoid full :latest / fat base images.
musl libc can break native deps (Python wheels, glibc binaries) and complicate DNS/locale handling.-slim: slightly larger but maximally compatible. Default to -slim unless you have measured a real size win and tested compatibility.FROM node:22.11.0-slim@sha256:<digest>. Tags are mutable; digests are not.AS builder, AS deps, AS runtime.CMD ["node","server.js"] — never the shell form CMD node server.js (it breaks signal handling and PID 1 semantics).USER 10001:10001. A numeric UID lets Kubernetes enforce runAsNonRoot even without /etc/passwd.COPY --chown=10001:10001 to set ownership without an extra layer; never chmod -R 777.apt-get update && apt-get install -y --no-install-recommends X && rm -rf /var/lib/apt/lists/*.--no-install-recommends (apt) / --no-cache (apk) / --frozen-lockfile (npm ci) for determinism.RUN --mount=type=secret, never ARG/ENV (they persist in image history and docker history leaks them).HEALTHCHECK for long-running services so orchestrators can detect liveness.WORKDIR explicitly; never rely on /.tini or --init (or a runtime that reaps zombies) when your process spawns children.references/best-practices.md for the full annotated checklist and rationale.USER.ADD for local files — use COPY. Reserve ADD for remote URLs/tar auto-extraction (and prefer not to).ARG/ENV or copied .env files — they leak via docker history and image layers.apt-get upgrade / unpinned latest — destroys reproducibility.RUN chmod/chown after COPY — doubles the data on disk across layers. Use COPY --chown.COPY . .) before installing deps — busts the cache on every source edit.CMD/ENTRYPOINT — your process won't receive SIGTERM, so graceful shutdown breaks..dockerignore — bloats context, slows builds, risks leaking .git/credentials.references/best-practices.md — exhaustive, annotated best-practice checklist with rationale, BuildKit cache-mount and secret-mount recipes, and a security/supply-chain section.examples/go-multistage.Dockerfile — minimal scratch-based Go image (non-root, pinned, static).examples/node-multistage.Dockerfile — Node.js multi-stage with npm ci, cache mounts, distroless final.examples/python-multistage.Dockerfile — Python slim with venv copy and non-root user.templates/Dockerfile.template — language-agnostic fill-in-the-blanks multi-stage template.templates/dockerignore.template — sensible default .dockerignore.scripts/check_dockerfile.py — zero-dependency static auditor that flags root users, unpinned bases, secrets in ARG, shell-form CMD, missing .dockerignore, and more.Use the examples/templates as the starting skeleton, then apply the workflow and run scripts/check_dockerfile.py plus hadolint before finishing.
Other measured skills in the registry, with their headline benchmark lift.