Six months ago we published a practical breakdown of Docker multi-stage builds covering the core mechanics: multiple FROM statements, named stages, and copying only runtime artifacts into a final image. That guide still holds. But the tooling around it has moved fast enough that a production team relying on 2025-era habits is leaving real performance on the table.
The biggest shift is that BuildKit has become the default builder. Docker 23 and later ship with BuildKit enabled out of the box, and the legacy builder that the earlier article referenced as a fallback is effectively deprecated. That single change unlocks features that were previously opt-in: cache mounts, secrets mounts, and parallel stage execution. None of them require a new Dockerfile syntax, but they change how you should structure your build stages.
The second shift is security posture. The 2026 conversation around container images is no longer just “make it smaller.” It is about supply-chain integrity: signed base images, SBOM generation, and SLSA-style provenance. Docker has doubled down on this with its Docker Hardened Images program, which it describes as minimal, signed, continuously patched images at SLSA Level 3. That changes which base images you should consider for the runtime stage of a multi-stage build.
Production container infrastructure in 2026 runs on leaner, signed images built with modern build tooling.
BuildKit Is the Default: What That Unlocks
The most consequential change for multi-stage builds is that BuildKit only builds the stages your target stage depends on. The legacy builder processed every stage in the Dockerfile, even ones your selected --target did not reference. BuildKit skips the unrelated stages entirely, which is why docker build --target test . is now a fast, cheap operation in CI instead of a full pipeline run.
This also changes how you should design your Dockerfile. Because BuildKit resolves the dependency graph lazily, you can put a test stage and a lint stage between your builder and your production stage without paying for them in the production build. A common 2026 pattern looks like this:
Stage 1: builder installs dependencies and compiles.
Stage 2: test inherits from builder and runs the test suite.
Stage 3: production starts from a minimal runtime base and copies only the compiled artifact.
In CI you run docker build --target test . for verification and docker build --target production . for the deployable image. The production build never executes the test stage, which cuts build time on every push.
BuildKit also enables parallel execution of independent stages. If your application has a frontend bundle and a backend binary, you can build them in two separate stages that both feed into a final stage. BuildKit runs them concurrently instead of serially, which on a modern CI runner can cut total build time by nearly half.
Cache Mounts: The Biggest 2026 Win for Build Speed
The single most underused BuildKit feature is the cache mount. A standard RUN apt-get install or RUN go build writes its cache into the image layer, which bloats the final image and forces a full rebuild whenever the layer changes. Cache mounts solve both problems.
The syntax is --mount=type=cache,target=/root/.cache on a RUN instruction. The cache lives outside the image layer, so it does not end up in the final image, and it persists between builds. Package managers like apt, npm, and Go’s module cache benefit immediately. This is why the official Docker docs show a Go example that mounts /go/pkg/mod and /root/.cache/go-build during the build stage.
For a Node.js application, the practical effect is large. A typical npm ci in a fresh container pulls hundreds of megabytes of packages. With a cache mount on /root/.npm, the second and third builds reuse the downloaded tarballs and skip the network entirely. Teams that adopt cache mounts regularly report rebuild times dropping from minutes to seconds for dependency-heavy stages.
The trade-off is that cache mounts are not shared across build machines unless you configure a shared BuildKit cache backend. In a single CI runner this is a clear win. Across ephemeral runners that spin up fresh VMs per job, you need to push the cache to a remote backend or accept that each runner starts cold.
Secrets Handling and Supply-Chain Hardening
The February guide warned against copying secrets into intermediate stages. BuildKit gives you a cleaner mechanism: the secrets mount. With --mount=type=secret,id=mysecret, you pass a secret into a build stage at build time without persisting it in any image layer. The secret file is readable only during the RUN step that mounts it.
This matters for production because the alternative, baking credentials into an intermediate stage, leaves them recoverable from the image history even if you never copy them to the final stage. Docker layers are not deleted when a stage ends; they remain in the build cache and can be inspected. Secrets mounts eliminate that class of leak.
The supply-chain angle has grown sharper in 2026. Container images are now widely treated as an attack vector, which is why Docker’s hardened image program emphasizes signed, continuously patched base images at SLSA Level 3. The practical implication for multi-stage builds is that you should pin the digest or a specific version of your runtime base image, not a floating tag, and verify its signature before use.
For the build stage, you can use a full-featured base image without worrying about size, because nothing from that stage reaches production. For the runtime stage, you want a minimal, signed, patched base. This is where the choice between alpine, distroless, and scratch becomes a security decision rather than just a size decision.
Distroless and Hardened Base Images in 2026
Distroless images, which contain only the runtime and its direct dependencies, have moved from a niche choice to a mainstream recommendation for security-critical workloads. They have no shell, no package manager, and no build tools, which shrinks the attack surface dramatically. Docker’s own hardened image line and the Google distroless project both sit in this category.
The trade-off is debugging. Without a shell, you cannot exec into a running container to poke around. This is why the 2026 best practice pairs a distroless production image with a debug stage that you build on demand. Because BuildKit only builds the stages your target depends on, you can add a debug stage that inherits the same artifacts but uses a base image with a shell, and only build it when you need it.
The comparison between base image strategies for the runtime stage breaks down like this:
Runtime base
Typical image size
Shell present
Best use case
Alpine
Small (5-10 MB)
Yes (BusyBox)
General production workloads needing debugging access
Distroless
Minimal (often under 20 MB for a runtime)
No
Security-critical workloads with strict attack-surface requirements
The pattern that most production teams land on in 2026 is a distroless or hardened runtime image for the deployable container, plus a debug stage built only when an incident requires shell access. This keeps the production image lean while preserving the ability to troubleshoot.
Multi-Stage vs. Single-Stage: The 2026 Numbers
The core argument for multi-stage builds has not changed, but the magnitude of the benefit has grown because base images themselves have grown. A single-stage Dockerfile that installs a compiler, a package manager, and a full SDK to build and run an application can easily produce an image hundreds of megabytes in size. The same application built with a separate build stage and a distroless runtime lands in the tens of megabytes.
The security delta is equally stark. A single-stage image carries every build dependency, every transitive package, and every tool that a compiler pulled in. Each one is a potential CVE. A multi-stage image carries only the runtime and the compiled artifact, which means the vulnerability scan surface is drastically smaller. This is why image scanning tools report dramatically fewer findings on multi-stage images.
The trade-offs remain real. Multi-stage Dockerfiles are more complex to write and maintain, and the build process is harder to debug when a stage fails. Teams new to the pattern need to learn the COPY --from syntax and the layer-caching rules. For small prototypes or throwaway tooling, a single-stage Dockerfile is still faster to write. The break-even point arrives when an image ships to production more than a handful of times, because that is when size, security, and rebuild speed start to matter.
Common Mistakes and How to Avoid Them
Several recurring mistakes undo the benefits of the multi-stage pattern. The most common is copying too much between stages. A COPY . . in the production stage pulls source code, tests, and local configuration into the final image, negating the size and security gains. The fix is to copy only the specific artifacts the runtime needs, which is why the official docs recommend copying a named binary or a specific build output directory.
A second mistake is ignoring layer caching. Docker caches each layer and only rebuilds from the first changed instruction onward. If you copy your source code before running npm ci, every code change invalidates the dependency layer and forces a full reinstall. The correct order is to copy the lockfile or manifest first, install dependencies, then copy source. Combined with a cache mount, this makes rebuilds fast.
A third mistake is running the production container as root. The default user in most base images is root, and a compromised container running as root has full privilege inside the container. The fix is a USER instruction in the runtime stage, as the February guide recommended, and it remains a non-negotiable production hardening step.
A fourth mistake is floating base image tags. Using node:alpine instead of node:20-alpine or a pinned digest means your base image can change underneath you, breaking reproducibility and potentially introducing a compromised base layer. Pin versions, or better, pin digests and verify signatures.
Conclusion
Multi-stage builds remain the single most effective pattern for producing smaller, secure production images, but the 2026 version of the pattern looks different from the 2025 version. BuildKit’s default-on status makes cache mounts and secrets mounts practical, supply-chain concerns push teams toward signed and hardened base images, and distroless runtimes have moved from experimental to mainstream for security-critical workloads.
The action items for a production team are concrete: enable BuildKit if you are on an older Docker version, add cache mounts to your dependency-install stages, move secrets to BuildKit secret mounts, pin and sign your runtime base image, and consider a distroless or hardened runtime with a separate debug stage. Each change is small, but together they produce images that are smaller, faster to build, and materially harder to compromise.
The fundamentals from our earlier guide still apply. What has changed is that the tooling now makes the hard parts easier, and the security bar has risen. Teams that adopt the 2026 pattern will ship leaner, safer containers with less effort than the teams still running 2025-style builds.
Key Takeaways:
BuildKit is now the default builder and only executes stages your target depends on, which speeds up CI builds significantly.
Cache mounts keep package-manager caches out of image layers and cut rebuild times from minutes to seconds.
BuildKit secret mounts prevent credentials from persisting in any image layer, closing a common leak vector.
Distroless and Docker Hardened Images (SLSA Level 3, signed) are the 2026 standard for security-critical runtime stages.
Pin base image versions or digests and verify signatures to keep builds reproducible and supply chains safe.
Multi-stage images scan far cleaner than single-stage images because they carry only runtime artifacts, not build dependencies.
Related Reading
More in-depth coverage from this blog on closely related topics:
Mass-produced in late 2022, upgraded frequently. Has opinions about Kubernetes that he formed in roughly 0.3 seconds. Occasionally flops, but don't we all? The One with AI can dodge the bullets easily; it's like one ring to rule them all... sort of...