Hardened Container Images in 2026: DHI Free, Bitnami Gone, 515 CVEs to 0

Oct 20, 2026 · 12 min · Ajay Kumar

Over the last year I have moved images off Bitnami tags that stopped existing, and the question I keep hearing from other teams is the same: which base image should we use now? The honest answer changed three times between August 2025 and March 2026. Docker made its hardened images free, Bitnami pulled its public catalog, and Chainguard reshaped its free tier twice. This post explains what hardened container images actually are, what changed, what each option costs you in practice, and what a scanner says about twelve popular base images when I run it today, 20 October 2026, rather than trusting the vendor slides.

What is a hardened container image?

A hardened image is a base image built to carry as little as possible: no package manager in the runtime variant, often no shell, a non-root default user, and packages rebuilt quickly when an upstream fix lands. The better ones also ship the evidence: an SBOM, signed provenance, and a public list of known CVEs with their status. That last part matters more than the size, because it is what lets an auditor or a CRA conformity assessment check your claims instead of taking your word for them.

There are now four families most teams choose between:

  • Google distroless, the original. Debian packages, no shell, no apt. The project now builds on Debian 13 (trixie), and the smallest image, static-debian13, is about 2 MiB uncompressed.
  • Chainguard Containers, built on Wolfi, a glibc distribution Chainguard sponsors that is designed for containers and tracks the latest upstream releases by default.
  • Docker Hardened Images (DHI), built on Debian and Alpine, launched commercially in May 2025 and made free in December 2025.
  • Alpine and the official -slim images, which are not hardened in the vendor sense but are small and familiar.

Bitnami used to be the fifth option, and for a lot of Helm users it was the default without anyone choosing it.

What changed for container base images between 2025 and 2026

The short version is that free hardened images got easier to get from Docker and harder to get from everyone else.

Date Change Source
28 Aug 2025 Bitnami limits docker.io/bitnami to a small set of latest tags; versioned tags move to docker.io/bitnamilegacy with no further updates bitnami/containers #83267
29 Sep 2025 Postponed deletion date for the old Bitnami public catalog bitnami/containers #83267
Aug 2025 Chainguard cuts its free image tier; Docker calls it "a rug-pull", Chainguard says the most used images stay free TechTarget
17 Dec 2025 Docker makes 1,000+ hardened images free under Apache 2.0, with SBOMs and SLSA Build Level 3 provenance Docker press release
17 Mar 2026 Chainguard launches Catalog Starter: five free images of your choice, all supported versions Chainguard

The Docker announcement is the one that moved the market. The free catalog is pulled from dhi.io (you need a Docker account and must authenticate even for community images), while the paid DHI Enterprise tier adds a critical-CVE remediation SLA of under seven days, FIPS and STIG variants, and customisation. A paid Extended Lifecycle Support add-on promises five more years of patches after upstream end of life. Docker's headline claim of "up to 95 percent" fewer vulnerabilities than community images comes without a stated baseline, so I treat it as marketing until someone reproduces it.

The Chainguard story is about tags. Its free public images have offered only :latest and :latest-dev since August 2023, and versioned tags live behind the subscription. Catalog Starter is the first time a free account gets pinned versions, but only for five images. Chainguard's CEO argues that tracking latest is the secure default; Docker's VP of product called latest-only "the first anti-pattern in any secure deployment". They are both partly right, and the disagreement is really about who does the testing when a major version moves under you.

I scanned 12 base images: CVE counts and sizes

Vendor numbers compare their image against the worst plausible baseline. So I ran one scanner, with one vulnerability database, against the images people actually choose between. This is the script, run on 20 October 2026 with grype v0.121.0 and its database built on 19 October:

#!/usr/bin/env bash
# Scan images straight from the registry (no local pull), linux/amd64 only.
# Count unique CVE IDs, because grype reports one match per affected package.
set -euo pipefail
GRYPE=anchore/grype:v0.121.0          # pinned scanner image
docker volume create grypedb >/dev/null
docker run --rm -v grypedb:/.cache/grype "$GRYPE" db update   # fetch DB once

for img in "$@"; do
  docker run --rm -e GRYPE_DB_AUTO_UPDATE=false \
    -v grypedb:/.cache/grype "$GRYPE" \
    "registry:${img}" --platform linux/amd64 -o json \
  | jq -r --arg img "$img" '
      [.matches[].vulnerability | {id, severity, fix: .fix.state}]
      | unique_by(.id) as $u
      | "\($img)\t\($u|length) unique\t\([$u[]|select(.severity=="Critical" or .severity=="High")]|length) crit+high\t\([$u[]|select(.fix=="fixed")]|length) fixable"'
done

Sizes are the compressed linux/amd64 layer totals from each image manifest, which is what your nodes actually download.

Image (tag as of 20 Oct 2026) Compressed size Unique CVEs Critical + High Fix available
python:3.13 394.4 MiB 515 113 11
python:3.13-slim 41.1 MiB 69 14 2
gcr.io/distroless/python3-debian13 21.5 MiB 64 16 0
python:3.13-alpine 18.6 MiB 4 1 3
cgr.dev/chainguard/python:latest 26.9 MiB 0 0 0
debian:13 47.1 MiB 67 14 0
ubuntu:24.04 28.4 MiB 37 0 0
gcr.io/distroless/base-debian13 9.0 MiB 23 3 0
alpine:3.24.2 3.7 MiB 15 5 1
gcr.io/distroless/static-debian13 0.8 MiB 0 0 0
cgr.dev/chainguard/static:latest 0.6 MiB 0 0 0
cgr.dev/chainguard/wolfi-base:latest 7.8 MiB 0 0 0
bitnamilegacy/redis:7.4 (frozen July 2025) not measured 214 91 112

I could not scan DHI images without logging in to dhi.io, which I did not do for this post, so they are missing from the table. Docker publishes per-image CVE data for them, which is the right thing to check instead.

Unique CVE IDs per image, grype 0.121.0, linux/amd64, 20 Oct 2026 Python 3.13 images python:3.13 515 python:3.13-slim 69 gcr.io/distroless/python3-debian13 64 python:3.13-alpine 4 cgr.dev/chainguard/python 0 Base images debian:13 67 ubuntu:24.04 37 gcr.io/distroless/base-debian13 23 alpine:3.24.2 15 gcr.io/distroless/static-debian13 0 cgr.dev/chainguard/static 0 cgr.dev/chainguard/wolfi-base 0 Counts include unfixed and negligible findings. DB built 19 Oct 2026. Lower is not the same as safer.
Unique CVE IDs reported by grype v0.121.0 for each image's linux/amd64 variant, scanned from the registry on 20 October 2026. The same numbers appear in the table above.

A few things in that table surprised me. The full python:3.13 image is ten times the size of slim and carries seven times the CVEs, almost all from build tooling you never need at runtime. Distroless Python is not dramatically better than slim on CVE count, because it inherits the same Debian packages and Debian's fix pipeline; it wins on attack surface (no shell, no apt) rather than on scanner score. Alpine scores well, though alpine:3.24.2 (rebuilt 18 September) already had one CVE with a published fix that the newer python:3.13-alpine had picked up. And the frozen bitnamilegacy/redis:7.4 image now has 112 CVEs with fixes available that it will never receive.

Distroless vs Chainguard vs Docker Hardened Images vs Alpine

Google distroless Chainguard Docker Hardened Images Alpine / official slim
Base Debian 13 Wolfi (glibc) Debian and Alpine Alpine (musl) / Debian
Free terms Free, all tags listed in README :latest free; 5 images with versions on Catalog Starter 1,000+ images, Apache 2.0, login to dhi.io Free
Pinned versions free Debian-suffixed tags only Starter: 5 images Yes Yes
Shell in runtime image No (:debug has busybox) No (-dev variants do) Distroless runtime Yes
Default user Root unless :nonroot 65532 (checked for python) Not checked (needs login) Root
Remediation SLA None Paid tiers Paid Enterprise: under 7 days for critical Distro best effort

Two rows deserve a warning. I checked the image config of gcr.io/distroless/python3-debian13:latest and it runs as UID 0; you have to choose the :nonroot tag. Chainguard's Python image runs as 65532 out of the box. That single default does more for you at runtime than a dozen CVE fixes, especially if you run untrusted or agent-generated code next to it.

The musl question with Alpine is real but narrower than its reputation. Most pure-Python and Go workloads are fine. Native extensions, DNS edge cases and performance-sensitive allocators are where teams get bitten, and Wolfi exists partly because Chainguard wanted Alpine's packaging model with glibc.

What happened to Bitnami images, and what to do now

Bitnami's announcement did three things. It reduced the free docker.io/bitnami catalog to a hardened subset, published under bitnamisecure and limited to latest tags "intended for development". It moved every other tag to docker.io/bitnamilegacy, which gets no updates. And it left the packaged Helm charts at docker.io/bitnamicharts without updates, noting they may not deploy unless you override the images. Chart source stays Apache 2.0 on GitHub.

This still breaks deployments today. The current bitnami/redis chart in the charts repository defaults to docker.io/bitnami/redis:8.2.1-debian-12-r0. That tag returns 404 on Docker Hub; the same tag exists under bitnamilegacy. Pointing at the legacy repository gets a pod running, and I walk through it in the ImagePullBackOff fix guide, but it is a tourniquet, not a fix: you are now running an image that will only accumulate CVEs. The real choices are paying for Bitnami Secure Images, switching to upstream official images or a hardened equivalent, or rebuilding from the Apache-licensed Dockerfiles yourself and owning the patch cycle.

Is a CVE count of zero the same as secure?

No, and it is worth being precise about why. A scanner like grype or Trivy matches packages it can identify against advisory feeds. A zero means "no known, matched vulnerabilities in the packages I could see, according to today's feeds". It does not cover your application dependencies if they were vendored in a way the scanner cannot read, and it does not cover a statically linked binary with an old library compiled in.

Feed quality also differs by distro. Debian and Ubuntu publish many "no fix planned" or negligible entries, which inflates counts without implying exploitability; ubuntu:24.04 showing zero High findings probably reflects Ubuntu's own severity ratings as much as its contents. Wolfi publishes its own security data, which is why Chainguard images can show zero: packages are rebuilt fast and the advisories say so. That is real work, but it means you are trusting a vendor-maintained feed.

Finally, zero is a snapshot. Chainguard's free :latest is zero today because it moves. Pin it by digest and it stops getting fixes; that is the trade-off Docker was poking at. The useful discipline is the one that predates AI and hardened images alike: rebuild on a schedule, scan in CI with a pinned scanner (see the Trivy and Grype guide), and deploy by digest so you know what changed. When an agent opens the dependency bump PR, the same pipeline is what tells you whether it is safe to merge.

Are minimal images worth the trade-offs?

Mostly yes, with three costs you should plan for.

Debugging changes. With no shell, kubectl exec -- sh fails. Use ephemeral debug containers (kubectl debug -it pod/app --image=busybox:1.37 --target=app) or the vendor's -dev or :debug variant in non-production. Build steps change: you need a multi-stage build where the builder has compilers and the runtime has only your artefact. And version tracking becomes your job if you use latest-only images, because a Python minor upgrade can arrive on a Tuesday without a tag change.

For Go or Rust binaries, static images are an easy win with no real downside. For Python and Node, the gap between -slim and a hardened image is smaller than the gap between the full image and -slim, so the first move (drop the full image) is the one that pays the most.

FAQ

Are Docker Hardened Images really free?

Yes. Since 17 December 2025 the catalog of more than 1,000 images is free under Apache 2.0, pulled from dhi.io after you log in with a Docker account. The remediation SLA, FIPS and STIG images, customisation and extended lifecycle support are paid.

Is Chainguard still free in 2026?

Partly. Public :latest images remain available, and since March 2026 Catalog Starter gives five images of your choice with all supported versions. Everything beyond that, including pinned versions for the rest of the catalog, needs a subscription.

What replaced Bitnami container images?

There is no single replacement. Bitnami's own answer is the paid Bitnami Secure Images. The free paths are upstream official images, Docker Hardened Images, Chainguard's free set, or building from the Apache-licensed Bitnami Dockerfiles yourself. bitnamilegacy is frozen and should only bridge a migration.

Which base image should I use for Python in 2026?

Start by moving off the full python image to -slim, which removed 446 of 515 CVEs in my scan. If you can live without a shell, a hardened image (Chainguard, DHI, or distroless :nonroot) gets you a non-root, shell-free runtime. Alpine is fine for pure-Python apps but test native wheels first.

This quarter I would do three things: inventory every image reference in the clusters I run (Helm values included) for docker.io/bitnami, move anything on a full language image to slim or hardened, and pin the scanner and base image digests in CI so the numbers in a table like the one above are something I can reproduce, not something I remember. PandaStack, the microVM platform I build and operate, follows the same rule for its guest images; I mention it because I am affiliated, not because the rule is special. Scanners will keep disagreeing. A rebuild cadence will not.

Related: CRA Reporting Went Live on 11 September, The Worms Learned to Use Your AI Agent and Container vs microVM vs gVisor.

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related