DevOps & Cloud

Docker multi-stage builds: smaller production images with a clearer purpose

Understand how build stages separate compilation from runtime, and why image contents, secrets and verification still need deliberate decisions.

An organized warehouse illustrating the packaging and delivery of software artifacts.

An organized warehouse illustrating the packaging and delivery of software artifacts.

Understand what a production image must do

A container image packages the files and configuration needed to start a process. Development often needs compilers, test tools and source files that the running application does not require. Shipping all of them can increase image size and make the runtime harder to inspect. Before changing a Dockerfile, identify the actual startup command, required files and operating-system dependencies. The goal is a repeatable runtime artifact. A small image that cannot start correctly or lacks required assets is not a successful optimization.

Separate building from running

A multi-stage Dockerfile uses one stage to prepare the application and another to package the runtime result. Build stages may install development dependencies and run compilation. The final stage copies only the selected outputs and required runtime material. Name stages clearly so their purpose remains understandable. The separation helps avoid carrying an entire build workspace into production, but it does not decide which files are safe or necessary automatically. Those decisions still depend on how the specific application is built and started.

Identify framework-specific runtime output

A Next.js deployment, a compiled NestJS service and a Spring Boot application do not share the same artifact structure. Review the framework and project configuration rather than copying a Dockerfile blindly. Determine whether static assets, templates or native libraries must accompany the compiled output. A successful compilation inside the builder does not prove the final image contains everything needed. Verify the application's actual startup path and an important request against the final stage, including files that are loaded only after the first page or endpoint is accessed.

Keep dependency installation reproducible

Use the project's lockfile and an installation mode appropriate for repeatable builds. Separate dependency copying from frequently changing source where that helps cache reuse. Do not sacrifice correctness for a fast cached build: changes to dependency declarations must invalidate the appropriate step. Review whether install scripts or native packages need special handling. Production dependencies should match the runtime environment. An application that works on a developer's machine may fail if the final image uses an incompatible platform or omits a library required by a compiled module.

Control the build context deliberately

The build context determines which local files can be sent to the builder. Use an appropriate ignore file to exclude irrelevant output, local caches and private environment files. A later command that deletes a secret does not make copying it into an earlier image layer safe. Review both the context and the Dockerfile's copy operations. Keep the application inputs explicit enough that another maintainer can understand them. Reducing accidental context content can also make builds faster, but preventing private or irrelevant files from entering the process is the more important design objective.

Treat build secrets and runtime secrets differently

A build may need temporary credentials to retrieve a private dependency. The running application may need different credentials to contact a service. Neither should be hardcoded into the Dockerfile or committed configuration. Use the build and deployment platform's appropriate secret mechanisms and avoid printing sensitive values in logs. Verify what becomes part of generated application output, especially for frontend configuration that is intentionally exposed to browsers. A multi-stage design helps separate work, but does not automatically prevent a secret from being embedded in a compiled file.

Run with the permissions the process requires

Review the runtime user, writable directories and mounted paths. Where the application supports it, running without unnecessary privileges can reduce the consequences of a compromised process. File ownership must still allow legitimate startup and temporary operations. Do not simply switch users and assume every framework's cache or upload directory will work. Test the final filesystem behavior under the chosen user. Deployment permissions and mounted storage need to align with the image design so the application does not require emergency permission changes after release.

Choose and maintain the runtime base image

A minimal base image can reduce included components, but compatibility and maintainability matter. Understand the operating system, runtime version and native dependencies. Use a deliberate update process and scan the final artifact rather than only the builder. An image digest can identify a precise base, while a maintained process is still needed to adopt updates. Smaller does not mean invulnerable. The useful outcome is a known, supportable set of runtime contents that your team can update and verify when dependencies or security requirements change.

Example: a build succeeds but static assets disappear

A web application compiles successfully in the builder stage. The final stage copies the server output but omits a required public directory. The process starts, yet images and downloadable files return errors. Testing only the build command would miss this. Start the final image and request an actual page plus its important assets. Then document which output directories are required and why. This example shows that multi-stage builds are a packaging design problem, not merely a way to make a reported image size smaller.

Integrate the artifact into the release process

Build a versioned image and identify the artifact used in each environment. Avoid rebuilding with uncontrolled differences between validation and release. The deployment process should supply environment-specific configuration through an agreed mechanism and retain a previous known artifact for recovery. Readiness checks and application observability remain necessary; a completed container start is not proof that the service can perform useful work. We can connect image preparation with an existing CI pipeline without replacing reliable deployment tooling merely to introduce a different container workflow.

Workshop: inspect what reaches the final image

Choose one service and list its actual runtime files, commands and writable paths. Create a builder and a final stage, then compare the resulting contents with that list. Start the final image using representative configuration and test an important journey, not only a health endpoint. Check that local environment files and build credentials are absent. Include a dependency change in a second build to verify cache behavior. Record the tradeoffs so another developer can maintain the Dockerfile without reintroducing the complete development workspace.

Frequently asked questions

Does every application need many stages? Use stages where they clarify preparation and runtime responsibilities. Does a small image guarantee security? No; contents, configuration and update practices still matter. Should we copy a Dockerfile from another framework? Adapt it to the actual runtime output and verify the final image. What can Tuplify Studio help with? Container packaging, build pipelines, deployment configuration and recovery procedures for business applications. Begin with one reproducible service and expand the pattern only after its final artifact is tested.

TUPLIFY STUDIO

What we can build for you

Tuplify Studio can containerize Next.js, NestJS and Spring Boot applications, improve build pipelines and prepare maintainable deployment and recovery workflows.

Let’s discuss your project

Go further · official documentation

Docker — Multi-stage builds
  • Docker
  • Multi-stage builds
  • DevOps
  • Container security

Tuplify Studio · Educational guides created with AI assistance. Examples are illustrative.