Skip to content
docker-multi-stage logo

Docker Multi-Stage Builds Skill

docker-multi-stage

Multi-stage builds for optimized, minimal production images with build/runtime separation

SKILL.md

Full skill instructions

Docker Multi-Stage Builds Skill

Create optimized, minimal production images using multi-stage builds with language-specific patterns.

Purpose

Reduce image size by 50-90% by separating build dependencies from runtime, following 2024-2025 best practices.

Parameters

ParameterTypeRequiredDefaultDescription
languageenumYes-node/​python/​go/​rust/​java
targetstringNoruntimeBuild target stage
base_runtimestringNo-Custom runtime base image

Multi-Stage Patterns

Node.js (Alpine + Distroless)

# Build stage
FROM node:20-alpine AS builder
WORKDIR /​app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production

# Runtime stage (distroless = minimal attack surface)
FROM gcr.io/​distroless/​nodejs20-debian12 AS runtime
WORKDIR /​app
COPY --from=builder /​app/​dist ./​dist
COPY --from=builder /​app/​node_modules ./​node_modules
USER nonroot
CMD ["dist/​index.js"]

Python (Slim + Virtual Environment)

# Build stage
FROM python:3.12-slim AS builder
WORKDIR /​app
RUN python -m venv /​opt/​venv
ENV PATH="/​opt/​venv/​bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.12-slim AS runtime
WORKDIR /​app
COPY --from=builder /​opt/​venv /​opt/​venv
ENV PATH="/​opt/​venv/​bin:$PATH"
COPY . .
USER nobody
CMD ["python", "main.py"]

Go (Scratch = Smallest Possible)

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /​app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /​app/​server

# Runtime stage (scratch = 0 base size)
FROM scratch AS runtime
COPY --from=builder /​app/​server /​server
COPY --from=builder /​etc/​ssl/​certs/​ca-certificates.crt /​etc/​ssl/​certs/
USER 65534
ENTRYPOINT ["/​server"]

Rust (Musl for Static Linking)

# Build stage
FROM rust:1.75-alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /​app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

# Runtime stage
FROM scratch AS runtime
COPY --from=builder /​app/​target/​x86_64-unknown-linux-musl/​release/​app /​app
USER 65534
ENTRYPOINT ["/​app"]

Java (JRE Only Runtime)

# Build stage
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /​app
COPY . .
RUN ./​gradlew build --no-daemon

# Runtime stage (JRE only, not JDK)
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /​app
COPY --from=builder /​app/​build/​libs/​*.jar app.jar
USER nobody
ENTRYPOINT ["java", "-jar", "app.jar"]

Size Comparison

LanguageBeforeAfterReduction
Node.js1.2GB150MB87%
Python900MB120MB87%
Go800MB10MB99%
Rust1.5GB5MB99.7%
Java600MB200MB67%

Error Handling

Common Errors

ErrorCauseSolution
COPY --from failedStage not foundCheck stage name
not found at runtimeMissing libsUse alpine, not scratch
permission deniedNon-root userCOPY --chown

Fallback Strategy

  1. Start with alpine instead of scratch/​distroless
  2. Add required libraries incrementally
  3. Use ldd to identify missing dependencies

Troubleshooting

Debug Checklist

  • All required files copied to runtime stage?
  • SSL certificates included for HTTPS?
  • User/​group exists in runtime image?
  • Build artifacts correctly located?

Debug Commands

# Check final image size
docker images myapp:latest

# Inspect layers
docker history myapp:latest --no-trunc

# Compare with baseline
dive myapp:latest

Usage

Skill("docker-multi-stage")

Assets

  • assets/​Dockerfile.node-multistage - Node.js template
  • assets/​Dockerfile.python-multistage - Python template

Related Skills

  • docker-optimization
  • dockerfile-basics