A Docker multi-stage build is a technique that uses multiple FROM instructions in a single Dockerfile to create smaller, more secure, and efficient container images.
The core idea is "build fat, ship thin":
- Stage 1 (The Build): Use a heavy image with all the compilers, tools, and source code needed to build your app.
- Stage 2 (The Runtime): Use a lightweight image and only copy the final compiled application (the artifact) from the first stage.
Why use it?
- Smaller Size: Final images only contain what's needed to run, often reducing size by 90% or more (e.g., from 800MB to 20MB).
- Better Security: No compilers, source code, or extra packages are left in production, reducing the "attack surface".
- Simplicity: You manage everything in one file instead of separate build scripts.
Simple Example (Go Application)
This Dockerfile creates a tiny binary in the first stage and runs it in a minimal second stage.
# STAGE 1: Build the app
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp main.go
# STAGE 2: Create the final lean image
FROM alpine:latest
WORKDIR /root/
# Copy ONLY the binary from the 'builder' stage
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Build Command
You build a multi-stage image the same way as a regular one
docker build -t my-optimized-app .
To build only a specific stage (useful for debugging), use the --target flag:
docker build --target builder -t my-app-build-only .
Are these Dockerfiles are separately created? or coded in single Dockerfile?
after building the docker file, will it be a single image? and is the from:alpine in stage 2 is taking the only Stage 1?
1. Are they separate files?
No. All the stages are written in one single Dockerfile. You just use the FROM instruction multiple times within that same file.
2. Will it be a single image?
Yes. When the build finishes, Docker creates one single image based on the very last FROM stage. All the intermediate layers and tools from the earlier "build" stages are discarded. You won't see them in your local image list.
3. Does FROM alpine in Stage 2 take Stage 1?
No. FROM alpine tells Docker to start with a fresh, clean, tiny OS. It does not automatically include anything from Stage 1.
- The Magic Link: The only way Stage 2 gets anything from Stage 1 is when you explicitly ask for it using:
- COPY --from=builder /source/path /destination/path
- Think of Stage 1 as a temporary kitchen where you cook the meal, and Stage 2 as the clean plate where you only put the finished food. You throw away the messy kitchen (Stage 1) and only keep the plate (Stage 2).
But why we need stage 2? if we have builder already? does the stage two creates the lean image based on Stage 1 and alpine:latest
You need Stage 2 because the Builder stage is usually huge. It contains "heavy" tools like compilers, package managers, and your entire source code that you don't actually need to run the app.
Think of it like this:
- Stage 1 (The Factory): Has the welding machines, blueprints, and scrap metal.
- Stage 2 (The Showroom): Only has the finished car.
Does Stage 2 create the image based on Stage 1?
No. Stage 2 creates the image based only on alpine:latest.
It starts completely blank. It only becomes "based" on Stage 1 for a split second when you reach in and grab the finished executable file (the artifact) using the COPY --from command.
Why not just use the Builder image?
If you only used Stage 1 (the Builder), your final image would be massive (e.g., 800MB) because it keeps all the junk used during the build. By switching to Stage 2 (alpine), you ditch the junk and keep only the 5MB app, resulting in:
-
Faster Downloads:Pulling a 10MB image is faster than 800MB. -
Better Security:Hackers can't use your own compilers or source code against you if they aren't there. -
Less Storage:Your server stays clean.
Would you like to see a size comparison between a single-stage and a multi-stage Node.js or Python image?
Then Why Not Just Use The Stage 2 Alone?
Because Stage 2 (the lean image) doesn't have the tools to build your app.
If you use alpine alone, it is a "bare bones" box. It doesn't have a Go compiler, a Java JDK, or a C++ builder. If you try to run a build command inside Stage 2, it will fail because the "tools" are missing.
Here is the catch:
- Stage 1 has the Tools (heavy) but you don't want to ship them.
- Stage 2 has the Small Size (light) but it can't build anything.
The "Aha!" Moment
You use Stage 1 to do the "dirty work" (compiling the code). Once the code is turned into a single executable file (an artifact), you move that file into Stage 2.
If you didn't have Stage 1:
- You would have to compile the code on your personal laptop.
- Then, you'd have to hope your laptop's settings match the server's settings (which often causes "it works on my machine" bugs).
By using both:
- Stage 1 ensures the build happens in a controlled, identical environment every time.
- Stage 2 ensures the final product is as tiny as possible.
Top comments (0)