ZodOps

Containers · 8 min · Aug 12, 2026

Hardening container images without slowing your pipeline

Distroless bases, digest pins, and a scan policy that fails the build only when the finding is reachable — not when the CVE feed is loud.

Most container hardening programs stall for the same reason: they treat the image as a compliance artifact instead of a build product. Scanners light up, tickets multiply, and the pipeline either becomes a museum of waivers or a bottleneck nobody trusts. The work that actually ships is narrower — shrink the attack surface, pin what you run, and fail the build only on findings you can act on this week.

Start from a base you can explain

If you cannot name the packages in the runtime image, you cannot triage a CVE in under ten minutes. Distroless or a stripped chiseled base is not a fashion choice; it is how you make “no shell, no package manager, no leftover apk cache” the default. Keep a debug variant of the same image for break-glass sessions, built from the same digest, and never let that variant into production namespaces.

Pin the runtime image and copy only the binary.
FROM gcr.io/distroless/static-debian12:nonroot@sha256:…
COPY --from=build /out/api /api
USER 65532:65532
ENTRYPOINT ["/api"]

Digest pins beat tags, every time

Floating tags are how “we rebuilt nothing” still becomes a different libc on Friday. Pin every FROM line to a digest, and regenerate those pins in a dedicated renovate-style job so humans are not editing hashes by hand. Admission should reject pods whose image is not a digest. If your cluster still pulls :latest, the rest of the hardening program is theater.

Fail on reachable risk, not on feed volume

A gate that fails on every HIGH CVE will be bypassed within a quarter. Score findings by whether the vulnerable component is present, loaded, and reachable from the workload’s network path. Ignore Windows-only CVEs in a Linux image. Ignore kernel CVEs in a userspace Go binary. Keep a short allowlist, dated, with an owner — not a spreadsheet of eternal exceptions.

  • Rebuild the image on a cadence (daily for internet-facing services) even when your app did not change.
  • Publish an SBOM with the image and sign both with the same identity the cluster already trusts.
  • Block root, privileged, and hostPath in admission; fix the two workloads that still need them instead of weakening the policy.
  • Measure pipeline time. If hardening added more than a couple of minutes, you are scanning the wrong layer or the wrong cache.

The pipeline should get faster as the image gets smaller. If it did not, you added scanners without removing work. That is the opposite of hardening.