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"]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.