October 27, 2024
Docker Multistage Builds


Docker Multistage Builds: A Deep Dive with Examples
What Are Multistage Builds?
Multistage builds in Docker allow you to use multiple FROM
statements in a single Dockerfile, where each stage can have its own base image and dependencies. The key benefit is that you can compile and build your application in an intermediate stage and then copy only the necessary artifacts to the final lightweight image.
This significantly reduces the final image size by excluding build tools, temporary files, and unnecessary dependencies.
Why Use Multistage Builds?
-
Smaller Final Image Size
- Traditional Docker builds include all dependencies (compilers, dev tools, intermediate files).
- Multistage builds discard everything except the final executable or required files.
-
Improved Security
- Fewer packages = fewer vulnerabilities.
- No unnecessary tools (e.g., gcc, make) in the production image.
-
Faster Deployments
- Smaller images pull and start faster in production.
-
Cleaner Build Process
- Avoid manual cleanup steps (like deleting cache files).
How Multistage Builds Work
A typical multistage build has:
- Build Stage – Installs compilers, dependencies, and builds the application.
- Runtime Stage – Uses a minimal base image and copies only the compiled binary or required files.
Example: Multistage Build for a Go Application
Before (Single-Stage Build – Bloated Image)
FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o myapp
CMD ["./myapp"]
Problem: The final image includes Go compiler (~800MB), source code, and build tools—unnecessary in production.
After (Multistage Build – Optimized Image)
# Stage 1: Build the application
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Stage 2: Create a minimal runtime image
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Result: Final image is based on alpine (~5MB) + the binary (a few MB).
Use Cases for Multistage Builds
-
Compiled Languages (Go, Rust, C++) Problem: Compilers increase image size.
- Solution: Build in one stage, copy binary to a minimal image.
-
Node.js Applications Problem: node_modules and dev dependencies bloat the image.
- Solution: Install dependencies in one stage, copy only production files.
Example:
# Stage 1: Install dependencies and build FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Stage 2: Production image FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/index.js"]
-
Python Applications Problem: pip install includes dev packages.
- Solution: Use a build stage for dependencies, then copy only necessary files.
Example:
# Stage 1: Install dependencies FROM python:3.9-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --user -r requirements.txt # Stage 2: Runtime image FROM python:3.9-alpine WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD ["python", "app.py"]
-
Java (Maven/Gradle) Applications Problem: JDK is huge (~500MB), but only JRE is needed in production.
- Solution: Build with JDK, run with JRE.
Example:
# Stage 1: Build with Maven FROM maven:3.8.6 AS builder WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn package # Stage 2: Run with JRE FROM openjdk:17-jre-slim WORKDIR /app COPY --from=builder /app/target/myapp.jar . CMD ["java", "-jar", "myapp.jar"]
Best Practices for Multistage Builds
- Name your stages (
AS builder
) for better readability. - Copy only what’s needed (binaries, configs, not
.git
or temp files). - Use
.dockerignore
to exclude unnecessary files. - Leverage caching by ordering layers smartly.
Conclusion
Multistage builds are essential for optimizing Docker images. They:
- Reduce image size by excluding build tools.
- Improve security by minimizing attack surfaces.
- Speed up deployments with leaner images.
Use them for compiled languages, Node.js, Python, and Java applications to keep your containers efficient and production-ready.
Would you like a real-world case study on this? Let me know! 🚀
Thank you for reading! I hope you found this post insightful. Stay curious and keep learning!
📫 Connect with me:
© 2025 Ayush Rudani