The average Docker Hub base image carries between 50 and 300 known vulnerabilities. Teams spend hours triaging scanner results, determining which CVEs are exploitable, and deciding which ones to accept as risk. Chainguard's pitch is simple: start with zero.
Chainguard Images consistently scan with zero known CVEs at the time of publication. This is not a gimmick or aggressive vulnerability suppression. It is the result of an entirely different approach to how container images are built, maintained, and distributed.
How Chainguard Achieves Zero CVEs
Built from Source, Daily
Chainguard does not repackage Debian or Ubuntu binaries. Every package in a Chainguard image is compiled from source using their own build infrastructure. When a CVE is fixed upstream, Chainguard rebuilds the affected package and publishes an updated image, often within hours.
Traditional distribution maintainers (Debian, Ubuntu, Red Hat) backport patches into their package versions, which introduces lag. A CVE might be fixed in the upstream project on Monday but not appear in the Debian package until the following week or later. Chainguard's build-from-source approach eliminates this lag.
Wolfi: Purpose-Built for Containers
Chainguard images are built on Wolfi, a Linux distribution designed specifically for container environments. Wolfi is not a general-purpose OS. It has no init system, no kernel, and no unnecessary packages. It exists to provide a minimal, up-to-date package set for container base images.
Key Wolfi characteristics:
- apk package manager (Alpine-compatible format)
- glibc-based (not musl, so no compatibility issues with Go CGO, Python C extensions, or Java native libraries)
- Rolling release: packages are continuously updated, not pinned to release cycles
- SBOM included: every package ships with an SBOM in SPDX format
- Signed packages: all packages are signed with sigstore/cosign
Minimal by Default
Chainguard images include only what the application needs. A Chainguard Python image has the Python interpreter, its required shared libraries, and nothing else. No shell (in the non-dev variants), no package manager, no debugging tools.
# Compare package counts
docker run --rm cgr.dev/chainguard/python:latest -- python -c "print('hello')"
# The image contains ~15 packages
docker run --rm python:3.12-slim -- python -c "print('hello')"
# The image contains ~100+ packages
Using Chainguard Images
Available Images
Chainguard provides images for common runtimes and tools:
# Static binaries (Go, Rust)
FROM cgr.dev/chainguard/static:latest
# Go applications
FROM cgr.dev/chainguard/go:latest AS builder
# ...
FROM cgr.dev/chainguard/static:latest
# Python
FROM cgr.dev/chainguard/python:latest
# Node.js
FROM cgr.dev/chainguard/node:latest
# Java
FROM cgr.dev/chainguard/jre:latest
# nginx
FROM cgr.dev/chainguard/nginx:latest
# PostgreSQL
FROM cgr.dev/chainguard/postgres:latest
Production vs Development Variants
Each image comes in two variants:
:latest— Production. No shell, no package manager. Minimal attack surface.:latest-dev— Development. Includes a shell and apk. Use for building, debugging, and CI.
# Multi-stage: dev for build, production for runtime
FROM cgr.dev/chainguard/go:latest-dev AS builder
WORKDIR /src
COPY . .
RUN go build -o /app ./cmd/server
FROM cgr.dev/chainguard/static:latest
COPY --from=builder /app /app
ENTRYPOINT ["/app"]
Non-Root by Default
All Chainguard images run as a non-root user by default:
docker run --rm cgr.dev/chainguard/static:latest whoami
# nonroot
docker run --rm cgr.dev/chainguard/python:latest id
# uid=65532(nonroot) gid=65532(nonroot)
No need for USER nonroot in your Dockerfile. It is the default.
Scanning Results Comparison
Real scan results for equivalent images:
# Official Python image
trivy image python:3.12-slim
# Total: 67 (0 critical, 4 high, 28 medium, 35 low)
# Chainguard Python image
trivy image cgr.dev/chainguard/python:latest
# Total: 0
# Official Node.js image
trivy image node:20-slim
# Total: 52 (1 critical, 3 high, 22 medium, 26 low)
# Chainguard Node.js image
trivy image cgr.dev/chainguard/node:latest
# Total: 0
Zero does not mean "forever zero." New CVEs are discovered daily. But Chainguard's rebuild cadence means the window between CVE disclosure and patched image is typically hours, not days or weeks.
SBOM and Provenance
Every Chainguard image ships with:
Build-Time SBOM
# Extract the SBOM attestation
cosign verify-attestation \
--type spdx \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer-regexp '.*' \
cgr.dev/chainguard/python:latest | jq -r '.payload' | base64 -d
The SBOM lists every package, its version, its source, and its license. This is not a post-build scan. It is a build-time manifest that is cryptographically attached to the image.
Signature Verification
# Verify the image signature
cosign verify \
--certificate-identity-regexp '.*chainguard.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
cgr.dev/chainguard/python:latest
Kubernetes admission controllers (Kyverno, OPA Gatekeeper) can enforce that only signed Chainguard images are deployed.
Migration Considerations
Compatibility with Existing Dockerfiles
Most Dockerfiles need minor adjustments:
Package installation: Chainguard uses apk (in dev variants), not apt:
# Before (Debian-based)
RUN apt-get update && apt-get install -y curl
# After (Chainguard dev variant)
RUN apk add --no-cache curl
User permissions: Since images run as non-root by default, file operations that assume root will fail. Adjust file ownership in your build stage.
Shell scripts: Production images have no shell. Convert shell-based entrypoints to the application binary directly.
Breaking Changes to Watch
- glibc version: Wolfi ships current glibc. Applications compiled against much older glibc versions may have compatibility issues.
- File paths: System files may be in different locations compared to Debian or Ubuntu.
- Missing utilities: Production images lack common tools. Build-time steps that rely on
curl,wget, orjqmust happen in a dev-variant build stage.
Cost and Licensing
Chainguard offers:
- Free tier: A selection of popular images, updated daily
- Chainguard Images (paid): Full catalog, SLA-backed update guarantees, extended support, and FIPS-validated images
For compliance-heavy environments (FedRAMP, PCI-DSS, HIPAA), the paid tier's FIPS images and SLA guarantees are often a hard requirement.
Chainguard vs Distroless vs Alpine
| Feature | Chainguard | Google Distroless | Alpine | |---|---|---|---| | CVE count | ~0 | Very low | Low | | C library | glibc | glibc | musl | | Package manager | apk (dev only) | None | apk | | Shell | Dev variant only | Debug variant only | Yes | | Update frequency | Daily | Weekly | Weekly | | SBOM | Built-in | Partial | No | | Signatures | cosign | cosign | No | | FIPS variants | Yes (paid) | No | No |
For teams that need the absolute minimal CVE count, built-in SBOMs, and glibc compatibility, Chainguard is the current best option. For teams that want free, open-source minimal images with less emphasis on zero-CVE, Google Distroless remains solid.
How Safeguard.sh Helps
Safeguard.sh tracks your container base image inventory and identifies opportunities to migrate from bloated base images to Chainguard or similar minimal alternatives. The platform verifies SBOM attestations on Chainguard images, monitors for the rare CVEs that do appear between rebuild cycles, and ensures signature verification is enforced across your clusters. For teams with mixed image sources, Safeguard.sh provides a unified view of vulnerability exposure across Chainguard, distroless, Alpine, and traditional base images, making it clear where your remaining risk concentrates.