Multi-stage Docker builds
separating build tooling from runtime.
a build stage compiles with the toolchain, the final stage uses a minimal base and copies only the artifact, cutting size and attack surface.
shipping compilers and source.
WHAT THIS TESTS This evaluates whether you understand that the tools needed to build software are not needed to run it, and how Docker multi-stage builds exploit that.
A GOOD ANSWER COVERS In a single-stage build you start from an image with the full SDK, compilers, and build dependencies, compile inside it, and ship the whole thing, so the final image carries the toolchain, source code, and intermediate artifacts that the running program never needs. A multi-stage build splits this. The first stage uses FROM golang:1.22 or a JDK image as a build environment, copies in source, and compiles the binary or jar. The final stage starts FROM a minimal base such as gcr.io/distroless, alpine, or even scratch for a static Go binary, and uses COPY --from=build to bring across only the compiled artifact. The result contains just the runtime essentials and your binary. Beyond shrinking the image from hundreds of megabytes to tens or less, the smaller image has a much smaller attack surface because compilers, package managers, and shells are absent, fewer CVEs to patch, faster registry pushes and pulls, faster cold starts and autoscaling, and no source code leaking inside the shipped image.
COMMON WRONG ANSWERS Manually deleting build tools with extra RUN rm commands, which still leaves them in earlier layers and the history. Shipping the SDK image to production. Thinking the only benefit is disk space, ignoring security and pull speed. Using scratch for a dynamically linked binary that then cannot find its libraries.
LIKELY FOLLOW-UPS What is a distroless image and when can you use scratch? How does COPY --from work and can you copy from an external image? How does this interact with layer caching? How do you debug a container with no shell?
ONE CONCRETE EXAMPLE A Go service built single-stage on golang:1.22 is about 800 MB. Refactored: stage one on golang:1.22 runs go build -o app, stage two is FROM gcr.io/distroless/static and does COPY --from=build /src/app /app with ENTRYPOINT ["/app"]. The final image drops to roughly 20 MB, contains no compiler or shell, pulls almost instantly, and exposes far fewer vulnerabilities.
Read the original → docs.docker.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.