Skip to content

What Is a Multi-Stage Docker Build and Why Does It Matter?

In Multi-Stage Build, each FROM is a standalone stage and only the artifact needed with COPY --from the final image; read the advantages, common mistakes, and test method.

Author Bipida Editorial Team Published
Share this article

If you need a build program to compiler, a development dependency, and a test tool, you don't necessarily need to take them all to production with the program.FROMIt starts and the final step only takes files that you've directly copied from previous stages.

Quick answer:Use one stage to install dependencies and build artifacts, another possible stage to test, and runtime stage to execute the same artifact.COPY --from=buildThe output is needed, not the entire toolkit file system. The result is often smaller image and lower package level, but speed, security, and accuracy are not measured by image size alone.

What's the problem with the single-step Dockerfile?

In single-step creation, the compiler, package manager, source, cache, and dependency development usually remain in the same execution image of the program. Deleting the file in the next order does not always erase the effect of the previous layer from history. The larger image is transferred later and has more unnecessary components to patch and review. However, if the program really does not have any additional build or dependency steps, a simple stage may be sufficient.

Every one of them.FROMIt's a border.

In the Dockerfile, each multi-stepFROMIt's a new stage.AS buildOr...AS testThe stages can have the same or different base. By default, the last stage is the build output, unless another target is selected.COPY --fromOr they'll move the same explicit mechanism to the next stage.

A clear example is compiled output.

FROM golang:1.26 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/service ./cmd/server

FROM gcr.io/distroless/static-debian12:nonroot AS runtime
COPY --from=build /out/service /service
USER nonroot:nonroot
ENTRYPOINT ["/service"]

In this example, the compiler and source are in the build phase, and only the executable file goes to runtime../cmd/serverAnd binaries are written without the need for CGO, not for every Go application. If the application needs a template, migration or certificate file, add them separately and knowingly.

Why isn't it just the smaller size?

A smaller final image can be transferred faster and have fewer components, but an over-minimal image may not have the necessary CA, timezone, or library. Breaking the program for a few megabytes is not worth saving. The goal is to remove what does not require runtime; not to blindly remove each package.

Where do we put the test?

You can write a separate stage for the test or run the test in the builder after dependency is installed. If the stage test does not have a dependency on the final stage path, the production build may succeed without running the test; then CI must explicitly target the stage test and then create the final image.

Build the cache and copy the order.

For many stacks, they first copy the manifest and lockfile, take the locked dependency, and then add the source. A cache mount can reduce build time, but depending on the toolchain, you should make sure the final artifact is not connected to a local non-transferable cache.

Are the stages run in parallel?

BuildKit can optimize independent stages and sometimes run in parallel, but the exact speed depends on the graphic dependency, cache, network, and builder resources. Multi-stage should not be defined as the promise of "always build faster"; it may even be longer at the start of a cold reconstruction.

Copy the artifact versus copy the whole environment.

WhenCOPY --from=build / /Or you copy large folders, virtually neutralizing the stage boundary. In interpreter programs, runtime may require library, bytecode, or installed packages. Create and copy them in a controlled manner; test the compatibility of runtime and path versions.

Stages are not the place for Secret Management.

Just because the secret is used in the builder alone is not enough.ARGOr...ENVIt can be left in metadata or build logs. To access the build time private, use the appropriate secret/SSH mount, do not allow the build tool to copy it into the artifact and scan the build image afterwards.

Development and production targets

A Dockerfile can have a development target with a debugger and a target production without it. This reduces runtime variation, but should not depend on the production volume source and hot reload development environment. Make the stage names meaningful and specify in the pipeline which target, what context, and what build argument is being built.

Selection of different bases

The builder may need a compiler and header; runtime only requires the running libraries of the program. For native applications, check that the binary built with libc, architecture, and library version is compatible in runtime. The file not found error for the existing binary sometimes comes from the interpreter or library, not the file itself. Test this difference in staging.

When you're producing a frontend output

For a frontend application, stage one can install locked dependency and build static files; stage runtime only provides build output with the appropriate web server. Note that the variable that is no longer secret when build is placed inside the bundle is seen in the user's browser. Separate the client's public settings from the server's private keys. Also, if the public path, base URL or cache policy is different between staging and production, the decision is made. Take a common artifact that you can promote or build an environment that you need; don't check the claim of a host image in all environments without it.

What should we measure?

Before and after the change, record the image volume transferred, the build time, the runtime and the startup time. If only the volume decreased but the build is unstable or the recovery time is longer, the operational optimization is not complete.

The path to a build is difficult.

  1. Determine which stage and which command the error occurred in.
  2. Context files and.dockerignoreCheck it out.
  3. Measure the presence of an output in the builder's expected path.
  4. Limit and review the list of files transferred to runtime.
  5. Check the architecture, library and license of the artifact.
  6. Test the final image with user, config and actual volume.

To fix the problem, temporarily target the stage builder; direct modification of the container production is not a repeatable method.

Common Mistakes

  • Unstable stage name or fragile numeric reference
  • Copy the entire file system builder to runtime.
  • The difference between build and execution architecture
  • Forget running time files like templates
  • Testing only at stages where CI does not make
  • Assuming it's safe because of the decrease in volume.
  • Secret storage in the build argument

When is it worth adding a multi-stage?

If build requires a compiler, a frontend tool, a development dependency, or a packaged artifact, separation is very logical. For simple images without build, measure the benefits with added complexity.Checklist of Dockerfile productionThe rest of the runtime decisions, like the user and the signal, are covered.The application is Dockerized.It can build pipelines to review implementation.

Common Questions

Are previous stages running on the server?

No, the final image stage is deployed unless you intentionally build and execute another target.

Does Multi-Stage zero out vulnerabilities?

No, runtime dependencies, base image and program code still need to be scanned and patched.

Can I copy the file from another stage?

- Yes, I did.COPY --fromDesigned to transfer artifact between stages, test the path and permission of the file.

Is multi-stage build always faster?

No, cache and graph are the defining steps. The main advantage is the separation of the built-in output from the runtime environment.

How to Write a Production-Ready Dockerfile
Design the Dockerfile to produce, base, build multi-phase, cache, secret, non-root, signal, healthcheck and final image test.