Bird
Raised Fist0
MLOpsdevops~5 mins

Multi-stage builds for smaller images in MLOps - Commands & Configuration

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
Introduction
Building container images can create large files if all build tools and files stay in the final image. Multi-stage builds let you use multiple steps to build your app, but only keep the final needed files. This makes your images smaller and faster to download.
When you want to compile code inside a container but don't want the compiler in the final image.
When your app needs temporary files or tools only during build time, not at runtime.
When you want to reduce image size to save bandwidth and storage.
When you want to improve security by excluding build tools from the final image.
When you want to speed up deployment by having smaller images to pull.
Config File - Dockerfile
Dockerfile
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o my-app

FROM alpine:3.18
WORKDIR /app
COPY --from=builder /app/my-app .
CMD ["./my-app"]

This Dockerfile has two stages:

  • builder: Uses the golang image to compile the Go app.
  • final: Uses a small Alpine Linux image and copies only the compiled app from the builder stage.

This keeps the final image small by excluding the Go compiler and source code.

Commands
Builds the Docker image using the multi-stage Dockerfile in the current directory. The image is tagged as my-app:1.0.
Terminal
docker build -t my-app:1.0 .
Expected OutputExpected
Sending build context to Docker daemon 5.12MB Step 1/6 : FROM golang:1.20 AS builder ---> 3a2f3b1f4c7a Step 2/6 : WORKDIR /app ---> Using cache ---> 7c9f1e2a3b4d Step 3/6 : COPY . . ---> 1a2b3c4d5e6f Step 4/6 : RUN go build -o my-app ---> Running in 9f8e7d6c5b4a Removing intermediate container 9f8e7d6c5b4a ---> 8a7b6c5d4e3f Step 5/6 : FROM alpine:3.18 ---> 5d8f7e6c4b3a Step 6/6 : COPY --from=builder /app/my-app . ---> Using cache ---> 2b3c4d5e6f7a Successfully built 2b3c4d5e6f7a Successfully tagged my-app:1.0
Shows the size and details of the built image to verify it is smaller than a full build image.
Terminal
docker images my-app:1.0
Expected OutputExpected
REPOSITORY TAG IMAGE ID CREATED SIZE my-app 1.0 2b3c4d5e6f7a 10 seconds ago 12MB
Runs the built image to verify the app starts correctly with only the necessary files.
Terminal
docker run --rm my-app:1.0
Expected OutputExpected
No output (command runs silently)
--rm - Automatically remove the container after it stops
Key Concept

If you remember nothing else from this pattern, remember: multi-stage builds let you separate build steps so only the final needed files go into your container image, making it smaller and cleaner.

Common Mistakes
Copying all source files directly into the final image instead of using a builder stage.
This makes the final image large and includes unnecessary build tools and files.
Use a builder stage to compile or build your app, then copy only the final output into a clean base image.
Not naming the build stage and trying to copy from an unnamed stage.
Docker cannot find the source files to copy, causing build errors.
Name your build stage with AS and use that name in the COPY --from= stage.
Summary
Use multi-stage builds to separate building your app and creating the final image.
Build your app in a full environment, then copy only the needed files to a small base image.
This reduces image size, improves security, and speeds up deployment.

Practice

(1/5)
1. What is the main benefit of using multi-stage builds in Docker?
easy
A. They enable Docker images to run on any operating system without modification.
B. They create smaller and cleaner Docker images by separating build and runtime stages.
C. They automatically update the base image to the latest version.
D. They allow running multiple containers simultaneously.

Solution

  1. Step 1: Understand multi-stage build concept

    Multi-stage builds separate the build environment from the runtime environment in Dockerfiles.
  2. Step 2: Identify the benefit of separation

    This separation removes unnecessary build tools from the final image, making it smaller and cleaner.
  3. Final Answer:

    They create smaller and cleaner Docker images by separating build and runtime stages. -> Option B
  4. Quick Check:

    Multi-stage builds = smaller images [OK]
Hint: Multi-stage builds reduce image size by splitting build and runtime [OK]
Common Mistakes:
  • Confusing multi-stage builds with running multiple containers
  • Thinking multi-stage builds update base images automatically
  • Assuming multi-stage builds change OS compatibility
2. Which of the following is the correct syntax to start a new stage named 'builder' in a Dockerfile?
easy
A. FROM ubuntu AS builder
B. STAGE builder FROM ubuntu
C. NEW STAGE builder FROM ubuntu
D. BUILD STAGE builder FROM ubuntu

Solution

  1. Step 1: Recall Dockerfile multi-stage syntax

    To start a new build stage, Dockerfile uses 'FROM <image> AS <name>'.
  2. Step 2: Match correct syntax

    Only 'FROM ubuntu AS builder' matches the correct syntax for naming a stage.
  3. Final Answer:

    FROM ubuntu AS builder -> Option A
  4. Quick Check:

    Stage naming uses 'FROM ... AS ...' [OK]
Hint: Use 'FROM image AS name' to start a new build stage [OK]
Common Mistakes:
  • Using 'STAGE' keyword which does not exist
  • Writing 'NEW STAGE' instead of 'FROM ... AS ...'
  • Confusing 'BUILD STAGE' with Dockerfile syntax
3. Given this Dockerfile snippet, what will be the size effect 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 fail to build due to missing Go compiler in the second stage.
B. The final image will be large because it includes the entire Go build environment.
C. The final image will be small because it only copies the built binary from the builder stage.
D. The final image will include both Alpine and Go base images merged.

Solution

  1. Step 1: Analyze multi-stage build steps

    The first stage builds the Go binary using the full Go environment. The second stage uses a minimal Alpine image.
  2. Step 2: Understand what is copied to final image

    Only the compiled binary '/app/myapp' is copied from the builder stage to the final image, excluding build tools.
  3. Final Answer:

    The final image will be small because it only copies the built binary from the builder stage. -> Option C
  4. Quick Check:

    Copying only binary = smaller final image [OK]
Hint: Final image size shrinks by copying only needed files [OK]
Common Mistakes:
  • Assuming the entire build environment is included in final image
  • Thinking the build fails due to missing compiler in second stage
  • Believing base images merge into one large image
4. Identify the error in this Dockerfile snippet using multi-stage build:
FROM node:18 AS builder
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

FROM node:18
COPY --from=builder /app/dist ./dist
CMD ["node", "./dist/server.js"]
medium
A. The COPY command in the second stage has incorrect source path syntax.
B. The first stage is missing a WORKDIR declaration.
C. The CMD command is missing square brackets for JSON array syntax.
D. The second stage should use a smaller base image like 'node:18-alpine' to reduce size.

Solution

  1. Step 1: Review base images used in both stages

    Both stages use 'node:18', which is a full Node image including build tools.
  2. Step 2: Suggest optimization for smaller final image

    Using a smaller base like 'node:18-alpine' in the second stage reduces image size by excluding unnecessary tools.
  3. Final Answer:

    The second stage should use a smaller base image like 'node:18-alpine' to reduce size. -> Option D
  4. Quick Check:

    Use lightweight base images in final stage [OK]
Hint: Use lightweight base images in final stage for smaller images [OK]
Common Mistakes:
  • Thinking COPY syntax is incorrect when it is valid
  • Believing CMD needs different syntax here
  • Assuming WORKDIR is missing in first stage
5. You want to build a Python app with dependencies installed only during build, but keep the final image minimal. Which multi-stage Dockerfile snippet achieves this best?
hard
A.
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

FROM python:3.12-slim
COPY --from=builder /app /app
CMD ["python", "/app/app.py"]
B.
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "/app/app.py"]
C.
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "/app/app.py"]
D.
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "/app/app.py"]

Solution

  1. Step 1: Understand requirement for minimal final image

    Dependencies should be installed in a build stage, not in the final image, to keep it small.
  2. Step 2: Analyze options for multi-stage usage

    FROM python:3.12 AS builder
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    COPY . .
    
    FROM python:3.12-slim
    COPY --from=builder /app /app
    CMD ["python", "/app/app.py"]
    uses a builder stage to install dependencies and copies only the app to a slim final image, achieving minimal size.
  3. Final Answer:

    Option A correctly uses multi-stage build to keep final image minimal. -> Option A
  4. Quick Check:

    Install dependencies in builder, copy to slim final image [OK]
Hint: Install dependencies in builder stage, copy only needed files to slim image [OK]
Common Mistakes:
  • Installing dependencies directly in final image increasing size
  • Not using multi-stage build at all
  • Running app in builder stage instead of final stage