Why Distroless
Why Distroless
Why run a full Ubuntu image when your service only needs a static binary? A typical runtime image ships a shell, a package manager, and dozens of utilities — each one a path for an attacker who has already reached your container. Distroless images keep only the application and its runtime dependencies: no shell, no ssh, no package manager, no curl. Google maintains them (gcr.io/distroless/...) with variants per runtime (static, :python3, :nodejs20) plus :nonroot tags that execute as UID 65532 instead of root. The payoff is a much smaller image, far fewer CVEs to triage, and a container where docker exec gets an intruder nowhere.
Building Distroless Images
Building Distroless Images
Distroless bases contain no compiler, so you build in a builder stage and copy the artifact:
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/api
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app /app
ENTRYPOINT ["/app"]
CGO_ENABLED=0 produces a static binary the static base can run; dynamic binaries need a -cc variant with glibc. Language runtimes use their matching bases instead. Pin bases by digest in production so deploys are reproducible, and reach for the :debug tag — which adds a busybox shell — only while developing.
Debugging and Pitfalls
Debugging and Pitfalls
No shell changes operations. docker exec -it app sh fails immediately — that is the point. Debug locally with the debug tag: docker run --rm -it --entrypoint sh myapp:debug. Logs must go to stdout; there is no syslog or log directory to inspect inside the container.
Files are written as nonroot. The :nonroot variant runs as UID 65532, so writable paths need COPY --chown or a mounted volume.
Not a complete security strategy. Distroless removes OS tooling, not vulnerable libraries in your runtime or kernel bugs.
Interview answer: state the trade — smaller image and fewer entry points versus the loss of in-container debugging — and name the debug tag as your mitigation.
Quiz
1. What is the primary security benefit of a distroless image?
2. How do you interactively debug a distroless container?
3. Your Go binary links against libc. Which base should you use?
Flashcards
Question
What a distroless image contains
Click to reveal answer
Answer
Only the app and its runtime libraries — no shell, package manager, or utilities.
Question
Distroless nonroot user
Click to reveal answer
Answer
UID 65532; use :nonroot tags or set a USER so the process never runs as root.
Question
How to build for distroless
Click to reveal answer
Answer
Multi-stage Dockerfile: compile in a builder image, COPY the binary into the distroless stage.
Question
Debug tag purpose
Click to reveal answer
Answer
:debug adds busybox (sh) for troubleshooting; keep it out of production tags.
Question
What distroless does NOT fix
Click to reveal answer
Answer
Vulnerable app dependencies, kernel CVEs, and secrets in your code.
Revision Notes
Key Takeaways
- 1. Strip shells and package managers to remove post-exploitation tooling
- 2. Multi-stage builds are mandatory because distroless ships no compiler
- 3. Log to stdout — there is nothing inside the container to inspect
Interview Tips
- • Frame distroless as a tradeoff: smaller attack surface versus lost debugging tooling, and say how you compensate (:debug tag)
- • Mention UID 65532 / nonroot execution when asked how you remove root from containers
Cheat Sheet
Distroless = app + runtime only; no shell/pkg manager. Build via multi-stage; static → :static, dynamic → -cc; run :nonroot (UID 65532); debug with :debug tag; logs to stdout only.