Bird
Raised Fist0
Microservicessystem_design~7 mins

Multi-stage builds in Microservices - System Design Guide

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
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.

Practice

(1/5)
1. What is the main benefit of using multi-stage builds in container images?
easy
A. They reduce the final image size by separating build and runtime stages.
B. They allow running multiple containers simultaneously.
C. They automatically scale microservices based on load.
D. They enable containers to communicate over a network.

Solution

  1. Step 1: Understand multi-stage build purpose

    Multi-stage builds separate the build environment from the runtime environment to avoid including unnecessary build tools in the final image.
  2. Step 2: Identify the main benefit

    This separation reduces the final image size, making containers smaller and faster to deploy.
  3. Final Answer:

    They reduce the final image size by separating build and runtime stages. -> Option A
  4. Quick Check:

    Multi-stage builds = smaller images [OK]
Hint: Focus on build vs runtime separation for smaller images [OK]
Common Mistakes:
  • Confusing multi-stage builds with container orchestration
  • Thinking multi-stage builds scale services automatically
  • Assuming multi-stage builds enable container networking
2. Which of the following is the correct syntax to name a build stage in a Dockerfile for multi-stage builds?
easy
A. FROM node:18 WITH builder
B. STAGE node:18 builder
C. BUILD node:18 AS builder
D. FROM node:18 AS builder

Solution

  1. Step 1: Recall Dockerfile syntax for naming stages

    In Dockerfiles, the AS keyword is used after FROM to name a build stage.
  2. Step 2: Match correct syntax

    Only FROM node:18 AS builder correctly names the stage 'builder'.
  3. Final Answer:

    FROM node:18 AS builder -> Option D
  4. Quick Check:

    Stage naming uses 'AS' keyword [OK]
Hint: Look for 'FROM ... AS stageName' syntax [OK]
Common Mistakes:
  • Using incorrect keywords like BUILD or STAGE
  • Omitting the AS keyword
  • Placing stage name before FROM
3. Given the following Dockerfile snippet, what will be the size impact on the final image?
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

FROM alpine:latest
COPY --from=builder /app/myapp /usr/local/bin/myapp
CMD ["myapp"]
medium
A. The final image will be large because it includes the full Go environment.
B. The final image will be small because only the built binary is copied.
C. The final image will be empty because no files are copied.
D. The final image will contain both Go and Alpine layers.

Solution

  1. Step 1: Analyze the build stage

    The first stage uses the full Go environment to build the binary 'myapp'.
  2. Step 2: Analyze the final stage

    The final stage uses a minimal Alpine image and copies only the built binary from the builder stage.
  3. Step 3: Determine final image size impact

    Since only the binary is copied, the final image is small and does not include the Go environment.
  4. Final Answer:

    The final image will be small because only the built binary is copied. -> Option B
  5. Quick Check:

    Copying only binary = small image [OK]
Hint: Final image size depends on copied artifacts, not build tools [OK]
Common Mistakes:
  • Assuming build tools stay in final image
  • Thinking COPY copies entire build context
  • Confusing build and runtime stages
4. Identify the error in this multi-stage Dockerfile snippet:
FROM node:18 AS build
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

FROM node:18
WORKDIR /app
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]
medium
A. The stage name 'builder' used in COPY is incorrect; it should be 'build'.
B. The second FROM should use a lighter image like alpine.
C. The CMD syntax is invalid and will cause runtime error.
D. COPY command should copy from current stage, not from another.

Solution

  1. Step 1: Check stage naming consistency

    The first stage is named 'build' but the COPY uses '--from=builder', which does not exist.
  2. Step 2: Identify the error impact

    This mismatch causes a build failure because Docker cannot find the 'builder' stage.
  3. Final Answer:

    The stage name 'builder' used in COPY is incorrect; it should be 'build'. -> Option A
  4. Quick Check:

    Stage names must match exactly [OK]
Hint: Match stage names exactly in COPY --from [OK]
Common Mistakes:
  • Using wrong stage names in COPY
  • Ignoring case sensitivity in stage names
  • Assuming COPY defaults to previous stage
5. You want to optimize a microservice Docker image using multi-stage builds. The build stage requires many tools, but the runtime only needs the compiled binary and config files. Which approach best achieves a minimal, secure final image?
hard
A. Use a single-stage build with all tools and source code included.
B. Install all build tools in the final image to allow debugging in production.
C. Use a multi-stage build: build with full tools, then copy only binary and config to a minimal base image.
D. Build the binary outside Docker and copy it directly into the final image.

Solution

  1. Step 1: Understand build vs runtime needs

    The build stage needs many tools, but runtime only needs the binary and configs for security and size.
  2. Step 2: Choose best multi-stage build approach

    Using multi-stage builds to copy only necessary artifacts into a minimal base image reduces size and attack surface.
  3. Step 3: Evaluate other options

    Installing all tools in final image increases size and risk; single-stage builds are inefficient; building outside Docker loses reproducibility.
  4. Final Answer:

    Use a multi-stage build: build with full tools, then copy only binary and config to a minimal base image. -> Option C
  5. Quick Check:

    Multi-stage builds optimize size and security [OK]
Hint: Copy only needed files to minimal image for best results [OK]
Common Mistakes:
  • Including build tools in final image
  • Skipping multi-stage builds for simplicity
  • Building outside Docker losing environment consistency