Production Dockerfile and Container Review Template
A fill-in review for a container going to production — base image and its update path, build stages, non-root user, secrets, image size, health checks, signals and the vulnerability position you are accepting.
Markdown. No sign-up, no email.
Fill this in for any image that will run in production. Most container incidents trace back to one of five things: running as root, secrets baked into layers, a base image nobody updates, no health check, or a process that ignores shutdown signals. This page is those five plus the detail around them.
Image: _______________ Owner (person): _______________ Date: _______ Registry: _______________
1. Base image#
| Base image and tag | |
| Pinned by digest | yes / no |
| Why this base (not the default) | |
| Size | |
| Who rebuilds it when the base is patched | |
| Rebuild cadence |
🔴 :latest in production means the image you deploy on Friday is not the one you tested on Tuesday. Pin by digest, and own the update path deliberately — pinning without a rebuild schedule trades unpredictability for permanent staleness.
2. Build#
| Multi-stage build | yes / no |
| Build tools present in the final image | yes / no — which |
| Layer order optimised (dependencies before source) | yes / no |
.dockerignore present | yes / no |
| Final image size | |
| Reproducible from a clean checkout | yes / no |
Anything in the final image that is not needed to run the application: _______________
Compilers, package managers and shells left in a runtime image are attack surface and download time. Everything in the image is something you now maintain.
3. Secrets#
- [ ] No secrets in the Dockerfile
- [ ] No secrets in build arguments
- [ ] No secrets in any layer, including deleted files
- [ ] No
.env,.git, key material or cloud credentials copied in - [ ] Secrets injected at runtime from _______________
🔴 A file added in one layer and deleted in the next is still in the image. docker history and a layer inspection will find it, and so will anyone who pulls the image. If a secret was ever built into a published image, rotate it — deleting the image is not sufficient.
4. Runtime user and permissions#
| Runs as user | root / _____ (uid) |
| Filesystem read-only | yes / no |
| Writable paths required | |
| Capabilities dropped | |
| Privileged mode | yes / no — why |
| Host paths mounted | |
| Host network / PID namespace used | yes / no |
Running as root is the default and almost never necessary. Create a user in the Dockerfile, give it ownership of only what it needs, and switch to it before the entrypoint.
5. Process behaviour#
| PID 1 is the application, not a shell | yes / no |
| SIGTERM handled — graceful shutdown | yes / no |
| Shutdown grace period needed | |
| In-flight work finished or requeued on shutdown | |
| Restart policy |
A container that ignores SIGTERM is killed after the grace period. Under a rolling deploy that means dropped requests or half-finished jobs on every release — a defect that only appears in production, and only under load.
6. Health and observability#
| Liveness check | |
| Readiness check (distinct from liveness) | |
| Readiness actually checks dependencies | yes / no |
| Logs to stdout/stderr, not files | yes / no |
| Log format | |
| Metrics exposed |
Liveness answers "should this be restarted"; readiness answers "should this receive traffic". Wiring the same endpoint to both causes a restart loop the moment a dependency is briefly slow.
7. Resources#
| Request | Limit | |
|---|---|---|
| CPU | ||
| Memory |
Memory limit set from measurement, not a guess: yes / no What the application does when it hits the limit: _______________ (Usually: killed abruptly. If the runtime has its own heap setting, it must be told about the container limit or it will size itself against the host.)
8. Vulnerabilities#
| Image scanned on build | yes / no — tool |
| Scan result: critical ___ high ___ | |
| Accepted findings, with reasons | |
| Who reviews scan results, and when | |
| Base image rebuild triggers a rescan | yes / no |
Record the accepted risk explicitly. A scan whose output nobody reads is a build step, not a control.
9. Sign-off#
| Name | Date | |
|---|---|---|
| Built by | ||
| Security review | ||
| Approved for production |