Container Security

Distroless vs. Chainguard vs. Wolfi: Real Differences

A working engineer's comparison of Google Distroless, Chainguard Images, and Wolfi as base images, covering what actually breaks in production and what does not.

Shadab Khan
Security Engineer
6 min read

Every organization that starts taking container supply chain seriously ends up in the same argument: move off of full Debian or Ubuntu base images, but move to what? The three options people land on are Google's Distroless, Chainguard Images, and Wolfi. They are not interchangeable. The marketing makes them sound similar and the technical reality is that they solve different problems.

I have migrated workloads onto all three. Here is what actually matters when you have to live with the decision.

What is the actual difference between Distroless, Chainguard, and Wolfi?

Distroless is a set of minimal base images published by Google, built on top of Debian packages with almost everything stripped out — no shell, no package manager, no coreutils. You get a language runtime and the libraries it needs, nothing else. Chainguard Images are minimal, distroless-style images built on top of Wolfi, produced by Chainguard the company, sold as a commercial product with SLAs. Wolfi is the underlying Linux "undistribution" that Chainguard Images are built from — it is open source and you can use it directly, but it is designed for containers rather than general-purpose Linux.

The easy way to remember it: Distroless is Debian stripped down, Wolfi is a container-first distribution, and Chainguard Images are a commercial product built on Wolfi.

Practical consequence — if you want apk add to work during your build stage because you need to compile something, Wolfi gives you that and Distroless does not. Distroless images genuinely do not have a package manager. If your build needs to invoke a shell script in the final image, Distroless will not let you, and that is sometimes the whole point.

Which one has the smallest vulnerability surface?

Chainguard Images win on CVE count at any given moment, Wolfi is close behind, and Distroless is consistently worst of the three despite its reputation. I know that contradicts the marketing. The reason is that Distroless is based on Debian stable, which ships packages that sit for weeks or months with known CVEs before a point release. Wolfi is designed to update continuously, and Chainguard rebuilds and re-publishes images within hours of an upstream CVE being fixed.

What this looks like in practice: I ran a scan across fifty images across all three families using both Grype and Trivy in early 2026. The Distroless Python image had 7 high-severity CVEs in its glibc and OpenSSL layer. The equivalent Chainguard Python image had zero. The Wolfi-based equivalent built by my team had two, both for packages we had pinned to an older version for a compatibility reason.

The interesting nuance is that many Distroless CVEs are unreachable — they are in crypto libraries the Python runtime does not actually call in a typical workload. That is a reachability argument, not a base image argument, and you should not use base image choice to paper over the fact that your vulnerability program is noisy.

Which one is easiest to actually operate?

Wolfi is the easiest to live with, Chainguard is the easiest to adopt, and Distroless is the hardest to debug. This order matters more than most teams expect.

Wolfi is easy because it acts like a normal Linux when you need it to — apk works, packages have the structure you expect, glibc behaves like glibc. If you are building your own images from a Wolfi base, you have the same ergonomics you had with Alpine minus the musl weirdness. Chainguard is easy because the images come pre-built, signed, and with attestations, so you docker pull and start. Distroless is the hardest because when something does not work — and something always does not work — you cannot get a shell into the container and the error messages are terse.

The number of hours I have watched senior engineers burn trying to debug a Distroless container that exits immediately with no output is, at this point, measurable in engineering weeks. The fix is always the same: rebuild with the :debug tag that Google publishes, which gives you a busybox shell. Everyone forgets this exists.

When should you pay for Chainguard instead of using Wolfi directly?

Pay for Chainguard when you do not want to own the rebuild pipeline, when you need attestations signed by a specific identity for compliance, and when you want a vendor to be on the hook for CVE response time. Otherwise Wolfi on your own CI is fine.

Chainguard's commercial value is the FIPS-validated variants, the customer-facing attestation identity, and the operational SLA around CVE patching. If you have a customer asking for a FedRAMP artifact or a regulator asking who signed the base image and when, Chainguard is the fastest path to an answer. If you are a product team shipping internal services, rolling your own Wolfi base and rebuilding nightly in your CI achieves 80 percent of the security outcome for zero licensing cost, at the expense of your team owning the pipeline.

The cost model is also worth understanding before you pitch Chainguard to your finance team. Chainguard pricing scales per image family, which adds up fast if your organization has 40 microservices running 12 different language runtimes. Sometimes the cheaper answer is two engineers and a Wolfi build pipeline.

What are the real migration gotchas?

Three, consistently. First, anything that relies on /bin/sh in the final image. Kubernetes exec probes, init containers that run shell scripts, Helm chart hooks — they all silently assume a shell exists. Distroless does not have one. Chainguard and Wolfi can, depending on the image variant. Before you migrate, grep your manifests for exec: sh and command: ["/bin/sh".

Second, glibc versus musl. Wolfi uses glibc. Alpine uses musl. If you are migrating off of Alpine, Wolfi is nearly drop-in. If you are migrating off of Debian, Distroless and Chainguard are nearly drop-in. Crossing the glibc–musl line causes weird runtime errors in Go binaries with CGO enabled, Python wheels with native extensions, and anything using DNS.

Third, user IDs. Distroless runs as UID 65532 (nonroot) by default. Chainguard uses 65532 as well in most images. If your workload writes to a volume and the volume was provisioned by a Helm chart that hardcoded UID 1000, the container cannot write and everyone spends a morning confused. Check the security context before you migrate, not after.

How Safeguard.sh Helps

Safeguard.sh gives you the reachability overlay that base image choice alone does not — even a minimal Chainguard image can ship with a CVE in a library your application never calls, and reachability analysis cuts that noise by 60 to 80 percent so your team is not patching for the sake of the dashboard. Griffin AI compares your current base image against lower-CVE alternatives and suggests specific replacements based on the actual binaries in your image, working off SBOMs generated at build time with 100-level dependency depth. TPRM tracks the provenance chain of every base image layer, and the container self-healing module rebuilds and re-pushes your workloads automatically when an upstream Wolfi, Chainguard, or Distroless patch lands, so "our base image has a fix available" and "our production is running it" stop being different sentences.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.