BlogCI/CD

Distroless container images: shipping without a shell

Ship images with no package manager, no shell, and almost no attack surface — and still debug when you need to.

Apr 2, 2026·10 min readAdvanced

A distroless image contains your application and its runtime dependencies — and nothing else. No shell, no package manager, no coreutils. When an attacker lands a remote-code-execution bug, there is no sh, no curl, and no cat /etc/passwd to pivot with.

bash — there is no shelllive
docker run --rm -it gcr.io/distroless/static:nonroot sh
exec: "sh": executable file not found in $PATH
exactly the point — the toolbox an attacker reaches for is gone

What's gone, and why that helps

Attack surface
Typical base
bash / sh
apt / apk
curl, wget
coreutils
100+ CVEs to track
Distroless
no shell
no package mgr
no net tools
libc + your app
a handful of CVEs

Build on distroless

Dockerfile
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/app
FROM gcr.io/distroless/static:nonroot
COPY --from=build /app /app
USER nonroot
ENTRYPOINT ["/app"]
You can still debug
Use the :debug tag (adds busybox) or kubectl debug to attach an ephemeral container with tools — without baking them into production.

The tradeoffs

No shell means shell-form RUN/CMD will not work — use exec form. Health checks must call your binary, not curl. For most services that is a small price for a dramatically smaller attack surface.

Go deeper in a courseAdvanced container securityMinimal images, runtime hardening, and the escapes they stop.View course

Related posts