dockerfile-standard
Secure, cache-efficient Dockerfiles adapted to the project in front of you. Not a template dump.
Scope
Not here: Compose topology (compose-standard), Go layout and golangci (go-project-standard), Make target names (makefile-standard), when CI builds an image (ci-standard, though the Dockerfile content is still yours). K8s, registries, and runtime secret distribution are out of scope.
Workflow
1. Inspect the repository
Inventory before writing: languages, entrypoints, manifests and lockfiles, CGO and native deps, CA and timezone needs, writable paths, ports, health and shutdown behavior, existing Dockerfiles and ignores, Compose, CI, Make, architectures, registry and tags.
Say where you are uncertain.
2. Apply profile and standard
Read references/rules.yaml, the source of truth for rule IDs. Then:
- Full rules:
references/dockerfile-standard.md - Security, caching, reproducibility
- Language profiles: Go, Node
Greenfield: adapt a template from assets/ and replace every Adapt… marker. Never ship an unfinished placeholder.
Migrate: references/migration-guide.md, incrementally, measuring size and behavior before and after.
3. YAGNI
Nothing here is unconditional, and none of it goes in without evidence: Alpine, scratch, or distroless; HEALTHCHECK; every architecture; pinning every distro package; one mega-RUN; stripping every shell; COPY --link; maximum provenance on a local build.
4. Validate
python3 scripts/validate_dockerfile.py /path/to/Dockerfile
# image checks need Docker; smoke is opt-in:
bash scripts/validate_image.sh IMAGE_REF
bash scripts/validate_image.sh IMAGE_REF --smoke --read-only --cap-drop
Prefer docker build --check . where supported. Errors = fix. Warnings = human review. A static check is not proof of runtime security.
Validator logic is hardcoded today; the checks: blocks in rules.yaml are illustrative, not executed.
5. Report
- Runtime base and why it fits this project
- Stages before and after
- Image size before and after, measured, or say plainly that you did not measure it
- Secrets handling, runtime user, writable paths
- Multi-platform, or why single-platform
- Validations run, and which you could not run
- Exceptions taken and warnings left for review
Skip a line that has nothing to say rather than writing "not applicable".
Hard rules
- Never claim faster or smaller without a measurement.
# syntax=docker/dockerfile:1unless a documented labs feature is required.- Trusted, versioned bases. No floating
latest, no unqualifiedFROM node. - Secrets via BuildKit secret or SSH mounts. Never in
ARG,ENV, or aCOPYof credentials. - Production final stage runs as a non-root
USERunless excepted. - Exec-form
ENTRYPOINTandCMD. Entrypoint scripts end inexec. - Absolute
WORKDIR. - A
.dockerignorefor any non-trivial context. - Apps must not depend on this repo at image-build time.
Exceptions
# dockerfile-standard: except <rule-id> — <reason>
Every ignored MUST needs an exception line with a reason. An ignored SHOULD needs a brief rationale when it is not obvious.