Checklist · Docker

Container Image & Runtime Checklist

Build an image that is small, reproducible and safe to run — the build rules that matter, what must never be baked in, and the runtime settings that are wrong by default.

Markdown. No sign-up, no email.

Image / service: _______________ Date: _______

1. Base image#

  • [ ] Pinned by digest, not by tag — a tag moves, a digest does not
  • [ ] Minimal base chosen deliberately (distroless, slim, alpine) with the reason recorded
  • [ ] Base image comes from a source you would name in an audit
  • [ ] Base updated on a schedule, not when something breaks
  • [ ] Not latest anywhere in the file

An image built from FROM node:latest is a different image every week. The version running is not the version anyone approved.

2. Build#

  • [ ] Multi-stage build — build tools do not reach the final image
  • [ ] Dependency install is its own layer, before the source copy, so the cache survives a code change
  • [ ] .dockerignore present — .git, node_modules, secrets, test fixtures
  • [ ] Build is reproducible: same inputs, same image
  • [ ] No apt-get upgrade in the build — upgrade the base instead, or the image drifts
  • [ ] Package lists cleaned in the same layer they were created

3. Secrets — what must never be in an image#

  • [ ] No credentials in ENV, ARG, or any layer — layers are readable by anyone with the image, including deleted files
  • [ ] No .env file copied in
  • [ ] No private keys, certificates or tokens
  • [ ] No .git directory
  • [ ] Build-time secrets use the build secret mount, not ARG
  • [ ] Image scanned for secrets in the pipeline

A secret added in one layer and deleted in the next is still in the image. Deletion adds a layer; it does not remove one.

4. Runtime identity and filesystem#

  • [ ] Runs as a non-root user, created in the image
  • [ ] USER set — the default is root and the default is wrong
  • [ ] Read-only root filesystem, with writable volumes only where needed
  • [ ] No --privileged
  • [ ] Capabilities dropped, then added back only if required
  • [ ] no-new-privileges set

5. Size and layers#

  • [ ] Final image size recorded and justified
  • [ ] Nothing installed that is not needed at runtime — no compilers, no shells you do not use
  • [ ] Layers ordered least-changing to most-changing
  • [ ] Image inspected once by hand — you know what is in it

6. Configuration#

  • [ ] Configuration from environment or a mounted file, never baked in
  • [ ] The same image runs in every environment
  • [ ] Sensible defaults, with required values failing loudly at startup rather than defaulting to something wrong

7. Process behaviour#

  • [ ] One process concern per container
  • [ ] Process runs in the foreground, PID 1 handles signals, or an init is used
  • [ ] Responds to SIGTERM and shuts down cleanly within the grace period
  • [ ] In-flight work is finished or handed back on shutdown
  • [ ] Logs to stdout and stderr, not to files inside the container

A container that ignores SIGTERM is killed after the grace period. Every rolling update then drops whatever it was doing.

8. Health and resources#

  • [ ] Readiness check — can it serve right now?
  • [ ] Liveness check — the process only, never a dependency
  • [ ] Memory limit set, from measurement
  • [ ] CPU request set, from measurement
  • [ ] Behaviour under the memory limit understood — a runtime that does not see the limit will exceed it

9. Supply chain#

  • [ ] Image scanned for known vulnerabilities in the pipeline, and the result blocks
  • [ ] Dependency manifest committed and reviewed
  • [ ] Image signed, or its digest recorded in the deployment record
  • [ ] Registry access controlled and audited
  • [ ] Retention policy on old images — registries grow without limit

10. The traps#

  • [ ] Not treating the container as a small server — no SSH, no cron inside, no manual fixes
  • [ ] No state on the container filesystem — it is gone on restart
  • [ ] Not running as root because it was easier
  • [ ] Not copying the whole build context because .dockerignore was skipped
  • [ ] Not using the same image with an environment variable that changes behaviour dangerously

Sign-off#

NameDate
Built by
Reviewed by

Back to Docker