13 from Docker Hardened Images Migration: A Healthy Database That Kept Nothing
The hardened image move: a stack moves once every stateful service survives a recreate
By Vladimir Mikhalev Docker Captain · CNCF Ambassador · IBM Champion
A hardened image is smaller because it takes things away: a shell, a package manager, the tools a health check called, the user ID a compose file pinned, the directory a volume was mounted on.
The move is safe once each of those is checked on purpose, in a test that has seen the old setting fail, and once the release gate knows which scanner it reads.
- 1
Write, recreate and read every stateful service
On the new image, write a row, count the files on the volume, recreate the container, and read the row back. A healthy database proves nothing about where it writes; the trap test shows the quiet version.
- 2
Mount where the new image keeps its data
Read the image's guide for its data path. The hardened PostgreSQL keeps its cluster in a directory named after the major version, and Docker's guide says a new install mounts the parent. An install moved from the official image mounts the versioned directory, so its cluster is read where it is.
- 3
Keep a read-only root filesystem wherever it runs
In this stack it turned a silent data loss into an error at the first start.
- 4
Check every user ID against every pin and every volume
The hardened PostgreSQL runs as user 70 and the hardened Keycloak's files belong to 65532. Every pinned user in the compose file and the owner of every volume that already holds data are checked before the move.
- 5
Run every health check's command inside the new image
If the tool is not in the image (wget, grep, a shell loop), the check is already broken. Use the service's own health command or shell builtins.
- 6
Move an install with data in three steps
Stop the stack, its volumes stay. Hand the database volume to the new user once, with a script that refuses while anything uses the volume. Start with the override.
- 7
Decide the scanner before the first hardened digest
Write down which scanner the release gate reads and whether it trusts the vendor's vulnerability statements. Owner: whoever owns the release gate.
- 8
Give every puller credentials, and name who moves the pins
The hardened registry refuses anonymous pulls: every host, runner, mirror and bot needs a login. A pin nobody moves keeps last month's image under this month's findings.
Two scanners, one set of pinned images, 9 November 2026: Trivy without vendor statements, Scout with Docker's VEX
| Image | Official, Trivy | Hardened, Trivy | Official, Scout | Hardened, Scout |
|---|---|---|---|---|
| PostgreSQL | 2 critical, 93 high | 1 critical, 16 high | 4 critical, 35 high | 0 critical, 2 high |
| Keycloak | 0 critical, 7 high | 0 critical, 4 high | 1 critical, 14 high | 0 critical, 1 high |
| Traefik | 0 critical, 2 high | 0 critical, 0 high | 2 critical, 5 high | 1 critical, 4 high |
What shows it works
The runs and the test behind each step.
- The run of 28 October 2026: the trap test on both mount paths, the five sabotage tests (each takes one change out and requires that exact failure), and the upgrade test that moves an install with a Keycloak realm in it.
- The weekly scan of 9 November 2026: both scanners on the pinned digests, the counts in the table.
- The guide-copy test, 28 October 2026: the direct copy from Docker's guide, as written, starts the hardened image on an empty cluster.
- The pull request that adds the two missing steps to Docker's guide, proposed on 28 October 2026 (open as of 15 November 2026).
- docker-hardened-images-migration: the override, the volume move and every test.
The image took away what an attacker could use and what the compose file relied on.
The long version is the post Docker Hardened Images Migration. Docker, the guide to the hardened PostgreSQL image, checked 12 November 2026; Docker, Docker makes hardened images free, open and transparent for everyone, 17 December 2025.