# Container Estate Review — Sample

**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

| | |
|---|---|
| Scope | 61 images across 4 environments |
| Method | Registry inspection, manifest review, runtime configuration audit, scan results |
| Prepared for | Platform team and engineering leads |

## 1. Summary

| | Images | Share |
|---|---|---|
| Run as root | **38** | 62% |
| Base pinned by tag, not digest | **44** | 72% |
| Writable root filesystem | 51 | 84% |
| No memory limit set | 29 | 48% |
| Contain a package manager and shell | 47 | 77% |
| Built in the last 90 days | 33 | 54% |

**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

| | Median | Largest |
|---|---|---|
| Image size | 412 MB | 2.1 GB |
| Size after multi-stage rebuild (sampled 8) | 78 MB | 240 MB |
| Layers | 14 | 31 |

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 images | 19 |
| Pinned by digest | 17 |
| Pinned by tag | 44 |
| Base image with no named owner | **11** |
| Base built in-house, last updated 2024 | **3** |

**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 variable | 6 |
| Images containing a `.env` file | 4 |
| Images containing a `.git` directory | 9 |
| Images with a private key in a layer | **2** |
| Images where a secret was added then deleted in a later layer | **3** |

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 user | 23 |
| Root | **38** |
| Read-only root filesystem | 10 |
| `no-new-privileges` set | 12 |
| Capabilities dropped | 8 |
| Privileged | **2** |
| Handles SIGTERM correctly | 31 |

**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

| Priority | Finding | Images | Effort |
|---|---|---|---|
| 1 | Private keys in layers | 2 | Rotate and rebuild — done |
| 2 | Privileged container with no need | 1 | One line |
| 3 | Credentials in environment variables | 6 | Rotate, move to secret store |
| 4 | Running as root | 38 | One line each, plus fixing what breaks |
| 5 | Unowned base images with known vulnerabilities | 22 | Assign an owner, rebuild |
| 6 | Writable root filesystem | 51 | One 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.
