Sample Report · Docker

Container Estate Review — Sample

A worked example of reviewing every image an organisation runs — what is in them, what they run as, the base images nobody owns, and the findings ordered by what they would let an attacker do.

Markdown. No sign-up, no email.

This is an illustrative example. The organisation, images and figures are invented to show the shape of a review that produces a short, ordered list of work. Copy the structure.


Container estate review — August 2026#

Scope61 images across 4 environments
MethodRegistry inspection, manifest review, runtime configuration audit, scan results
Prepared forPlatform team and engineering leads

1. Summary#

ImagesShare
Run as root3862%
Base pinned by tag, not digest4472%
Writable root filesystem5184%
No memory limit set2948%
Contain a package manager and shell4777%
Built in the last 90 days3354%

Two findings account for most of the risk and both are one line each. Nothing in this review requires an architecture change.

2. What the images contain#

MedianLargest
Image size412 MB2.1 GB
Size after multi-stage rebuild (sampled 8)78 MB240 MB
Layers1431

Eight images were rebuilt with a proper stage boundary as an experiment. Median size fell 81% and no functionality changed, because what was removed was compilers, headers, package caches and test fixtures.

The 2.1 GB image is a Python service that installs a full build toolchain to compile one dependency. The toolchain is never used again after the build.

3. Base images#

Count
Distinct base images19
Pinned by digest17
Pinned by tag44
Base image with no named owner11
Base built in-house, last updated 20243

The three in-house base images last updated in 2024 are the most serious structural finding. They are used by 22 of the 61 images, they carry known vulnerabilities in packages nobody uses, and no one is responsible for rebuilding them. They were created by a team that has since been reorganised.

The 44 tag-pinned images are a reproducibility problem rather than a security one, and they are the reason four builds this quarter produced images that behaved differently from the previous build with no code change.

4. Secrets#

Found
Images with credentials in an environment variable6
Images containing a .env file4
Images containing a .git directory9
Images with a private key in a layer2
Images where a secret was added then deleted in a later layer3

The last row is the one to explain to teams. Those three images still contain the secret. Deleting a file adds a layer recording the deletion; every earlier layer, including the one holding the file, remains in the image and is readable by anyone who can pull it.

All 11 credentials found have been rotated. The images are being rebuilt.

5. Runtime configuration#

Images
Non-root user23
Root38
Read-only root filesystem10
no-new-privileges set12
Capabilities dropped8
Privileged2
Handles SIGTERM correctly31

The two privileged containers are both monitoring agents. One genuinely needs elevated access; the other was made privileged during an installation problem in 2024 and never reverted. Nobody knew.

Thirty images do not handle SIGTERM. These are the cause of the intermittent errors during deployments that three teams have reported separately and nobody has connected. The container is asked to stop, ignores the request because an unhandled signal to PID 1 is discarded, and is killed after the grace period with work in flight.

6. Ordered by what it would let an attacker do#

PriorityFindingImagesEffort
1Private keys in layers2Rotate and rebuild — done
2Privileged container with no need1One line
3Credentials in environment variables6Rotate, move to secret store
4Running as root38One line each, plus fixing what breaks
5Unowned base images with known vulnerabilities22Assign an owner, rebuild
6Writable root filesystem51One line, plus explicit volumes

7. Recommendations#

  1. Rebuild the two images containing keys and rotate. Already in progress.
  2. Revert the unnecessary privileged container. One line, and it removes the largest single piece of standing access in the estate.
  3. Assign an owner to the three in-house base images, or retire them onto a maintained public base. Twenty-two images depend on them and nobody is responsible.
  4. Non-root by default in the shared build template. New images inherit it; existing images convert as they are next touched rather than in a campaign.
  5. Fail the build on a tag-pinned base. Stops the reproducibility problem growing.
  6. Add SIGTERM handling to the shared service template. Fixes the deployment-window errors for every service that adopts it.

Deliberately not recommended: a full re-platform, or converting all 61 images at once. Items 1–3 are a week. Items 4–6 change the default so the estate improves as it is worked on.


Notes on using this format#

Count what images contain, not just whether they scan clean. A scanner reports known vulnerabilities. It does not tell you that 47 images ship a package manager and a shell that nothing uses.

Order by what the finding permits. Root in 38 images sounds worse than a private key in 2. The key is a credential an attacker already has.

Separate the one-line fixes from the campaigns. Six of the seven finding types are a single line per image. Presented together with a re-platform proposal, none of them get done.

Back to Docker