| name | implementing-container-image-minimal-base-with-distroless |
| description | Reduce container attack surface by building application images on Google distroless base images that contain only the application runtime with no shell, package manager, or unnecessary OS utilities. |
| domain | cybersecurity |
| subdomain | container-security |
| tags | ["distroless","container-images","minimal-base","attack-surface","docker","security-hardening","supply-chain","kubernetes"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
| nist_csf | ["PR.PS-01","PR.IR-01","ID.AM-08","DE.CM-01"] |
Implementing Container Image Minimal Base with Distroless
Overview
Google distroless images contain only your application and its runtime dependencies, without package managers, shells, or other programs found in standard Linux distributions. By eliminating unnecessary OS components, distroless images achieve up to 95% reduction in attack surface compared to traditional base images like ubuntu or debian. Major projects including Kubernetes itself, Knative, and Tekton use distroless images in production. As of 2025, Docker also offers Hardened Images (DHI) as an open-source alternative for minimal container bases.
When to Use
- When deploying or configuring implementing container image minimal base with distroless capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Common Misconfigurations & Verification
- Distroless image still runs as root: the plain
gcr.io/distroless/static-debian12 tag defaults to UID 0; only the :nonroot tag (UID 65534) drops root, and even then an explicit USER nonroot:nonroot is needed if you override the entrypoint. Verify: docker run --rm <img> id should not report uid=0.
- No
readOnlyRootFilesystem at runtime: distroless shrinks the image but a writable rootfs still lets an attacker drop files - pair it with securityContext.readOnlyRootFilesystem: true and allowPrivilegeEscalation: false.
- Setuid binaries copied in:
COPY --from=builder can pull in setuid helpers from the build stage. Verify with dive <img> or docker run --rm <img> + a find for mode 4000 in the build stage.
- Wrong base for linkage: a CGO/dynamically linked binary on
static-debian12 fails at runtime ("no such file"); use base- or cc-debian12. A glibc binary needs base, not static.
- Debug variant in production:
:debug ships a busybox shell at /busybox/sh, reintroducing exec - confirm prod manifests pin the non-debug, :nonroot tag by digest.