Docker and Compose
Why use a multi-stage Docker build?
Separate build tools from the runtime image to reduce size, attack surface and accidental secret leakage.
By AppLaunch Editorial · Reviewed 2026-08-25
Direct answer
The final image should contain the runtime, production dependencies and built application—not compilers, package caches or test tools. Multi-stage builds let one stage compile and another copy only the required artifacts.
What this means in plain English
Building an application may need compilers, development packages and source files that are not needed when it runs. A multi-stage Dockerfile uses one stage to build the app and a smaller final stage to run only the finished result.
The final image becomes smaller and contains fewer tools an attacker could use. It also makes the intended runtime files clearer. Still scan and test the final stage, because that is the image production actually runs.
What belongs in the final image?
| Builder | Toolchain, dev dependencies and compilation |
|---|---|
| Runtime | Minimum files and packages needed to execute |
| Verification | Scan and run the exact final stage |
A simple example
A Node app uses one stage to install all packages and create a production build. The final stage copies only the built files and production packages, then runs as a non-root user. Compilers, test tools and the original source tree are left behind.
What to do, step by step
1. Name builder and runtime stages.
Start here before buying anything or changing several settings at once. It gives you a clear starting point based on builder: toolchain, dev dependencies and compilation. Write the result down so you can compare it later.
2. Copy dependency manifests before source to improve cache reuse.
Use the same files, versions and settings that the real project will use. A quick test with an empty or different setup can look successful while completely missing the problem you are trying to solve.
3. Copy only built output into runtime.
Try the busiest realistic situation, not the easiest one. Include the people, data, traffic or background work you genuinely expect, then watch for slowdowns and errors rather than relying on a single headline number.
4. Run as a non-root user where possible.
Finish by checking the result against verification: scan and run the exact final stage. Keep the old setting or release available until you know the change works and can be reversed safely.
One more useful tip
Copy dependency files before frequently changed source files. Docker can then reuse the expensive dependency layer when application code changes but the lockfile does not.
Common mistakes and how to avoid them
Copying the whole builder filesystem.
This gives a misleading or unsafe result because it leaves out builder. A better approach is to name builder and runtime stages, then check the result before making the change permanent.
Leaving credentials in build layers.
This gives a misleading or unsafe result because it leaves out runtime. A better approach is to copy dependency manifests before source to improve cache reuse, then check the result before making the change permanent.
Testing only the builder stage.
This gives a misleading or unsafe result because it leaves out verification. A better approach is to copy only built output into runtime, then check the result before making the change permanent.
Words explained
- image
- The read-only package used to create a container. A good production image can be rebuilt and traced back to a specific release.
- secret
- A password, token or key that must not be committed to source control or exposed to users.
Quick checklist
- Name builder and runtime stages.
- Copy dependency manifests before source to improve cache reuse.
- Copy only built output into runtime.
- Run as a non-root user where possible.
Common questions
What is the simple answer?
The final image should contain the runtime, production dependencies and built application—not compilers, package caches or test tools. Multi-stage builds let one stage compile and another copy only the required artifacts.
What should I check first?
Start with builder: toolchain, dev dependencies and compilation. That is usually more useful than choosing from a marketing label or copying somebody else’s setting.
How can I make the change safely?
Name builder and runtime stages. Then change one thing at a time, keep a backup or old version, and use the same real-world test after each change.
What is the easiest mistake to avoid?
Copying the whole builder filesystem. Avoiding that one mistake makes the rest of the comparison much more trustworthy.