5.3.2.1 Final Minimal Image
A focused guide to Final Minimal Image, connecting core concepts with practical Docker and container operations.
A final minimal image is the goal of carefully designing a multi-stage build's last stage to contain as little as possible beyond exactly what the application needs to run, often using an extremely minimal base such as scratch or a distroless image when the application's runtime requirements allow it.
Achieving True Minimalism With Scratch
For statically compiled binaries with no external runtime dependencies, scratch — an entirely empty base image — represents the most minimal possible final image.
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app .
FROM scratch
COPY --from=builder /out/app /app
ENTRYPOINT ["/app"]
The resulting image contains literally nothing beyond the compiled binary itself.
Achieving Near-Minimalism With Distroless
For applications needing some minimal runtime support (certificate bundles, a runtime interpreter) without a full operating system, distroless base images provide a middle ground between scratch and a full distribution.
FROM gcr.io/distroless/python3
COPY --from=builder /app /app
CMD ["app/main.py"]
Verifying a Minimal Image Actually Works Correctly
A genuinely minimal image sometimes lacks tools needed for debugging or troubleshooting; testing the application's actual functionality thoroughly is necessary to confirm the minimalism hasn't broken anything the application actually depends on.
docker run --rm myapp:1.0
docker images myapp
Trade-offs of Extreme Minimalism
A scratch-based image has no shell, no package manager, and no debugging tools at all — troubleshooting an issue inside a running container becomes significantly harder, a trade-off worth weighing against the size and security benefits minimalism provides.
docker exec -it myapp /bin/sh
This command fails entirely against a scratch-based container, since no shell exists inside it.
Why a Final Minimal Image Matters
Achieving genuine minimalism in the final stage maximizes the size and security benefits of a multi-stage build, though the right degree of minimalism for a given application depends on weighing these benefits against the corresponding loss of in-container debugging convenience.