tezvyn:

Docker Multi-stage Builds: Slimmer, Faster Images

AI-drafted, machine-checkedSource: docs.docker.comadvanced

Treat your Dockerfile like a pipeline: build your app in one stage with all its tools, then copy only the final artifact to a clean production stage. This keeps images small by excluding build-time dependencies.

WHY IT EXISTS: Before multi-stage builds, creating small Docker images was clumsy. You either shipped a huge image containing all your build tools and source code, or you managed complex scripts to build an artifact outside Docker and then inject it into a second, minimal Dockerfile. This was inefficient and hard to maintain.

THE MENTAL MODEL: Think of it as a factory with two separate rooms. In the first room, the 'build stage', you have all your heavy machinery: compilers, SDKs, and testing libraries. You build your product there. Once finished, you move only the final product into a clean, empty second room, the 'final stage'. You then discard the first room and all its machinery. The final Docker image contains only the product, not the factory that built it.

HOW IT WORKS: You use multiple FROM instructions in a single Dockerfile. Each FROM line begins a new build stage, which you can name for clarity (e.g., FROM golang:1.21 AS builder). To move files between stages, you use the COPY --from flag, such as COPY --from=builder /app/my-binary .. Only the commands under the final FROM instruction are used to create the resulting image; all intermediate stages and their files are discarded, dramatically reducing the final image size.

WHEN TO USE IT: This is standard practice for compiled languages like Go, Rust, Java, and C#. It's also ideal for frontend web apps where you use a Node.js environment to build static assets (HTML, CSS, JS), but only need a lightweight web server like Nginx to serve them in the final image.

WHEN NOT TO USE IT: For purely interpreted languages like Python or Ruby, where the source code is the application and there's no separate compilation step, the benefits are less dramatic. However, it can still be useful for installing system-level dependencies or Python packages that require compilation, then copying only the necessary virtual environment to a smaller base image.

ONE CANONICAL EXAMPLE: A Go application build is a classic use case. The first stage uses the official golang image to compile the source code into a single static binary. The second stage starts from a minimal alpine base image and does nothing but copy the compiled binary from the first stage. The final image is tiny because it contains only the Alpine base and the single binary, not the entire Go compiler suite.

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.