Docker & ContainersI Β· FoundationsProduction lens
What production-readiness means here
What you'll learn
- Distinguish the production lens from the developer lens
- Recognise when a tutorial-grade pattern is unsafe in production
- Adopt the operational defaults we use throughout the course
- Audit a running host against those defaults with commands that can fail
- Explain what a restart policy responds to, and what it ignores
Prerequisites
None β start here.
Verified against Docker Engine 29.x Β· Docker Engine 28.x Β· Docker Compose 2.x Β· containerd 2.x Β· runc 1.2.x Β· BuildKit 0.20+ Β· Linux kernel 5.15+ Β· Ubuntu 24.04 LTS Β· Debian 12 (Bookworm) Β· 2026-08-12
The single most useful question a sysadmin can ask of any Docker content is:
Would this survive at 03:00 with no one awake to debug it?
Most Docker tutorials are written for developers who want their app to run on their laptop. Most production Docker environments are written for operators who need the platform to run unattended for months. The patterns overlap less than you think.
Production vs developer defaults
| Concern | Developer default | Production default | Verify with |
|---|---|---|---|
| Image base | latest | Pinned digest | docker inspect --format '{{.Image}}' |
| Container user | root | Non-root via USER directive | docker inspect --format '{{.Config.User}}' |
| Restart policy | none | unless-stopped | docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' |
| Health check | none | Defined HEALTHCHECK in image | docker inspect --format '{{.State.Health.Status}}' |
| Resource limits | none | CPU + memory + PIDs | docker inspect --format '{{.HostConfig.Memory}}' |
| Logging | json-file unbounded | rotated + shipped to central store | docker inspect --format '{{.HostConfig.LogConfig}}' |
| Storage | anonymous volumes | named volumes with backup plan | docker inspect --format '{{json .Mounts}}' |
| Networking | default bridge | user-defined bridge with segmentation | docker network ls |
| Secrets | env vars in compose | external secret manager | docker inspect --format '{{json .Config.Env}}' |
| Updates | docker pull && restart | staged rollouts with rollback | change record |
The fourth column is the point. A production default that nobody checks reverts within a quarter, quietly, one deployment at a time.
Auditing a host against the list
docker ps --format '{{.Names}}' | while read -r C; do
docker inspect "$C" --format '{{.Name}}
user={{if .Config.User}}{{.Config.User}}{{else}}ROOT(unset){{end}}
restart={{if .HostConfig.RestartPolicy.Name}}{{.HostConfig.RestartPolicy.Name}}{{else}}none{{end}}
memory={{if .HostConfig.Memory}}{{.HostConfig.Memory}}{{else}}UNLIMITED{{end}}
pids={{if .HostConfig.PidsLimit}}{{.HostConfig.PidsLimit}}{{else}}UNLIMITED{{end}}
health={{if .State.Health}}{{.State.Health.Status}}{{else}}NONE{{end}}
logdriver={{.HostConfig.LogConfig.Type}} opts={{if .HostConfig.LogConfig.Config}}{{.HostConfig.LogConfig.Config}}{{else}}DEFAULTS(unbounded){{end}}'
done$ ./docker-audit.sh/web
user=ROOT(unset)
restart=unless-stopped
memory=UNLIMITED
pids=UNLIMITED
health=healthy
logdriver=json-file opts=DEFAULTS(unbounded)
/postgres
user=999
restart=always
memory=2147483648
pids=512
health=NONE
logdriver=json-file opts=map[max-file:3 max-size:10m]Illustrative output
Two containers, six findings. None of them is an outage today, and
each is a plausible cause of one: an unbounded web can take the
hostβs memory, an unbounded log can take the hostβs disk, and
postgres has no health signal at all.
The five questions every production decision must answer
- What happens when this fails? Every component will fail. The question is what the failure looks like and how contained it is.
- What is the rollback? If a change makes things worse, how do you reverse it? Rollback is part of the change, not a separate concern.
- Who gets paged? Every change that affects observability, capacity, or reliability changes the on-call experience.
- What is the blast radius? A bug or attack affects a container, a host, a network segment, an entire fleet. The blast radius should be proportional to the value.
- Is the change observable? A change that cannot be measured is a change that cannot be reasoned about.
The five things production Docker always does
- Pins image versions by digest, not tag.
- Runs containers as non-root with the smallest capability set the workload needs.
- Defines a
HEALTHCHECKfor every long-running service. - Ships logs out of the host (to journald, syslog, Loki, or a sidecar).
- Backs up volumes and validates restores β quarterly, on a clock, with a clock.
What we will NOT do
This course will not:
- Pretend
docker run -v /:/hostis fine because βitβs a labβ. - Show you how to skip healthchecks to βsave timeβ.
- Treat
latestas a valid production tag. - Encourage disabling security features for convenience.
- Promise that Kubernetes will fix your Compose problems.
It will, instead, give you the operational foundation to run Docker the way it is run in environments that take production seriously.
Knowledge check
Knowledge check Β· 6 questions
Q1. A production Docker default that differs from a developer default is:
Q2. A HEALTHCHECK in the image is optional in production.
Q3. A container is running, reports `unhealthy`, and has `restart: unless-stopped`. What does Docker Engine do?
Q4. You deliberately stop a misbehaving container at 22:00. At 03:00 an unattended upgrade restarts dockerd. Which restart policy brings the container back?
Q5. The filesystem holding /var/lib/docker fills up because of unbounded json-file logs. Which of these are consequences? Select all that apply.
Q6. Log options are fixed at container creation, so adding rotation to daemon.json only affects containers created after the change.
Passing score: 75%. Answers are checked in this browser.