0
0
Microservicessystem_design~7 mins

Multi-stage builds in Microservices - System Design Guide

Choose your learning style9 modes available
Problem Statement
Docker images for microservices often become very large because they include all build tools, dependencies, and source code. This leads to slow deployments, increased storage costs, and longer startup times, which hurt scalability and developer productivity.
Solution
Multi-stage builds split the Docker build process into multiple steps. The first stages compile and build the application with all necessary tools. Later stages copy only the final artifacts into a clean, minimal image. This reduces image size and removes unnecessary build dependencies from the final container.
Architecture
┌───────────────┐       ┌───────────────┐
│ Build Stage   │──────▶│ Final Stage    │
│ (compile,     │       │ (copy artifacts│
│ dependencies) │       │  only)        │
└───────────────┘       └───────────────┘
         │                      │
         │                      │
         ▼                      ▼
  Large intermediate       Small final
  image with build tools   image without build tools

This diagram shows two stages: the build stage compiles the app and installs dependencies, then the final stage copies only the necessary files to create a small runtime image.

Trade-offs
✓ Pros
Significantly reduces final image size by excluding build tools and source code.
Speeds up deployment and startup times due to smaller images.
Improves security by minimizing attack surface in the runtime container.
Simplifies Dockerfiles by consolidating build and runtime steps.
✗ Cons
Build process becomes more complex and harder to debug due to multiple stages.
Longer build times as multiple stages run sequentially.
Requires Docker 17.05 or newer, limiting compatibility with older environments.
Use when your microservice images exceed 500MB or when deployment speed and security are priorities.
Avoid if your build process is very simple and images are already under 100MB, as added complexity may not justify benefits.
Real World Examples
Netflix
Uses multi-stage builds to create minimal container images for streaming microservices, reducing startup latency and improving security.
Uber
Employs multi-stage builds to separate build dependencies from runtime, enabling faster deployment cycles for their ride-hailing services.
Shopify
Uses multi-stage builds to optimize container sizes for their e-commerce platform microservices, reducing cloud storage costs.
Code Example
The before code installs all dependencies and source code in one image, making it large. The after code builds the app in a full Node image, then copies only the built files and production dependencies into a smaller Alpine image, reducing size and attack surface.
Microservices
### Before: Single-stage build
FROM node:18
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
CMD ["node", "server.js"]

### After: Multi-stage build
# Build stage
FROM node:18 AS build
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

# Final stage
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
RUN npm install --production
CMD ["node", "dist/server.js"]
OutputSuccess
Alternatives
Single-stage Docker builds
Build and runtime dependencies are combined in one image without separation.
Use when: Choose when your application is very simple and image size is not a concern.
Build artifacts outside Docker
Build happens on the host or CI system, and only artifacts are copied into a minimal Docker image.
Use when: Choose when you want to decouple build environment from Docker and have complex build pipelines.
Summary
Multi-stage builds split Docker image creation into separate build and runtime steps to reduce image size.
They improve deployment speed and security by excluding unnecessary build tools from the final container.
This pattern is essential for microservices with complex builds or large dependencies.