Ch. 13 · Docker

Docker Distroless and Minimal Images

Ship images without a shell or package manager, copy only the runtime, and accept the debugging trade-off.

~2 min readadvancedupdated Oct 5, 2026

A distroless image contains your application and its runtime dependencies but no shell, package manager or other OS utilities. Fewer packages mean a smaller image and a smaller attack surface, at the cost of the familiar tools you might use to debug inside the container.

Before you start

You should be comfortable with multi-stage builds and image layers. This article covers distroless images and their trade-offs.

Step-by-step walkthrough

Step 1: Use a distroless base for the runtime

Build in a full stage and copy the output into a distroless base such as gcr.io/distroless/nodejs. The runtime stage has no shell, so a command that spawns one fails. Only the language runtime and your files are present.

Step 2: Copy only what runs

Copy the built artifact, not the source or the package cache. A Node.js service needs its production dependencies and compiled output; a compiled binary needs only the binary and any shared libraries. Anything absent cannot be exploited.

Step 3: Plan for debugging

Without a shell you cannot exec in and poke around, so rely on logs, metrics and a non-distroless debug stage when needed. Keep a parallel debug image for troublesome environments, and make sure CA certificates and timezone data are present if the app needs them.

Worked scenario

The runtime stage is distroless and holds only the built output.

FROM node:22-alpine AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM gcr.io/distroless/nodejs22-debian12
COPY --from=build /app/dist /dist
CMD ["/dist/server.js"]
dockerfile

Walk through the example

The build stage has the full toolchain, and the runtime stage copies only dist. The distroless base provides the Node runtime so CMD runs, but there is no /bin/sh, so you cannot open a shell in production. The image is small and has few packages to patch.

Common mistake

Expecting a shell or package manager in the runtime stage, so a RUN or an exec fails. Another is missing CA certificates, which breaks outbound HTTPS, or forgetting timezone data for correct local time.

Verify the behavior

Run the image and confirm the app starts, then attempt docker exec ... sh and confirm there is no shell. Inspect the layer list and confirm the package manager is absent. Check that outbound HTTPS works, proving CA certificates are present.

Interview exercise

What do you give up with a distroless image?

Answer and reasoning

The interactive tools: no shell, no package manager and no common utilities, so you cannot exec in to inspect files or run diagnostics. That trade is usually worth it for a smaller attack surface and image, provided you have logs, metrics and a separate debug image to fall back on.

Continue learning

Compare build strategy in Docker multi-stage builds and runtime hardening in Non-root runtime. Read the Google distroless documentation and try the Docker interview questions.

More in Docker

read ✓Docker · hard

Docker Image Vulnerability Scanning

Scan images for known CVEs in CI, set a policy that blocks the build, and rebuild when base images are patched.

~2 min readread →
read ✓Docker · mid

Docker Registry Authentication

Authenticate to private registries with credential helpers, pull secrets in Kubernetes, and scoped tokens.

~2 min readread →
esc