🚀 Kubernetes Series – Day 3: Understanding Multi-Stage Docker Builds


“Building a container image shouldn’t mean shipping unnecessary weight. Multi-stage builds keep your containers lean, secure, and production-ready.”
What is a Multi-Stage Docker Build?
A Multi-stage Docker build is a powerful technique that lets you use multiple FROM
statements in a single Dockerfile. This enables you to separate the build environment (where your code is compiled or packaged) from the runtime environment (what actually runs in production).
This approach significantly improves Docker image quality by:
Reducing image size
Improving security
Making deployment faster and more efficient
Why Multi-Stage Builds Matter
In a traditional Dockerfile, everything, source code, dependencies, build tools, compilers, gets bundled into a single image. This leads to bloated, inefficient containers.
Problems with single-stage builds:
Build tools like compilers and package managers are included in the final image
Larger image sizes increase pull times and storage costs
More attack surface due to unnecessary software in production
Multi-stage builds solve this by:
Using a dedicated build environment in the first stage
Copying only the final, compiled application or assets into the second stage
Keeping the final image minimal and secure
How Multi-Stage Builds Work
Let’s break it down with an example using a Go application.
Example: Dockerfile for a Go Application
# Stage 1: Build the application
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY . ./
RUN go build -o myapp
# Stage 2: Create a minimal runtime image
FROM alpine:latest
WORKDIR /root/
# Copy the binary from the builder stage
COPY --from=builder /app/myapp .
# Define the default command
CMD ["./myapp"]
What’s Happening Here?
Stage 1 –
builder
Uses the full golang image with all necessary tools to compile the Go source code
Installs dependencies and builds the binary
Stage 2 – Final Image
Uses a minimal Alpine Linux image, which is small and fast
Copies the compiled binary from the first stage
Leaves behind everything else: source code, build tools, and dependencies
This approach results in a production image that is:
Much smaller in size
Free of build tools and sensitive source code
Faster to deploy and pull from registries
Benefits of Multi-Stage Docker Builds
Benefit | Description |
Smaller image size | Only the necessary artifacts make it to the final image |
Improved security | No compilers or unused tools included |
Cleaner deployment | Source code and intermediate files are left out |
Faster builds & pulls | Reduced image size leads to faster operations |
Better CI/CD pipelines | Keeps container builds efficient and focused |
When Should You Use Multi-Stage Builds?
Multi-stage builds are especially useful for:
Applications that require a build/compile step (Go, Java, Angular, React, etc.)
Projects that produce optimized production assets (minified JavaScript, bundled CSS, etc.)
Any production deployment where image size, security, and speed matter
Applying It to Other Languages
Java
Stage 1: Use Maven or Gradle image to build
.jar
or.war
Stage 2: Use OpenJDK runtime image to run the application
Node.js (React/Vue/Angular)
Stage 1: Use Node image to install dependencies and build production assets
Stage 2: Use Nginx or Alpine to serve the static files
Python
Stage 1: Use full Python image for installing packages
Stage 2: Use a lightweight base like
python:slim
for the runtime
Conclusion
Multi-stage Docker builds are a best practice for modern application deployment. By separating the build and runtime environments, you ensure that your images are lightweight, secure, and production-optimized.
In the Kubernetes world, deploying small and efficient containers at scale becomes critical. Multi-stage builds make that possible.
Subscribe to my newsletter
Read articles from Nishank Koul directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
