Chapter 14 • Production, Optimization & Security

Multi-Stage Builds & Image Optimization

In modern software engineering, the toolchains required to build code (TypeScript compilers, C++ build-essentials, Git, Python dev headers) are vastly different from what is needed to run it.Multi-stage builds enable you to maintain multiple FROM stages in a single Dockerfile, discarding gigabytes of intermediate build artifacts and delivering razor-thin, production-hardened images.

1. The Anti-Pattern: Single-Stage Image Bloat

When building applications in a single stage, all build tooling remains permanently frozen in the production image layers:

Network Latency
Sluggish Deployments

Pulling a 1.5 GB image across Kubernetes worker nodes slows down auto-scaling by minutes.

Cloud Costs
Storage & Egress

Cloud registries charge for stored gigabytes and internet egress bandwidth.

Security Exposure
Vulnerability Surface

Unused package managers, compilers, and curl binaries expose your container to CVE exploits.

2. Base Image Spectrum: From Ubuntu to Scratch

Selecting the right runtime base image dictates performance, memory footprint, and security posture:

Image BaseCompressed SizeC Standard LibraryShell / Package ManagerBest Fit Workload
ubuntu:24.04~75 MBglibcBash, apt, coreutilsLegacy monolithic tools requiring full OS packages.
alpine:3.19~5 MBmusl libcBusyBox sh, apkLightweight Node.js, Python, and shell automation scripts.
gcr.io/distroless/static~2 MBMinimalNO SHELL, NO PACKAGE MANAGERHigh-security production runtimes. Hackers cannot launch a shell!
scratch0 MBNone (Static)None (Empty filesystem)Statically compiled Go, Rust, or C++ binaries.

3. Production Statically Compiled Go / Rust Service (10 MB Image)

Compile your backend code inside a heavyweight Golang builder stage, and copy only the compiled binary into an empty scratch image:

# ---------------------------------------------
# STAGE 1: Compilation Environment (Heavy: ~800 MB)
# ---------------------------------------------
FROM golang:1.22-alpine AS builder

WORKDIR /src

# Cache dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy source code and build statically linked CGO-disabled binary
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /bin/api-server .

# ---------------------------------------------
# STAGE 2: Minimal Zero-Overhead Production (Tiny: ~12 MB)
# ---------------------------------------------
FROM scratch

# Copy SSL root certificates for outbound HTTPS calls
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copy only the compiled binary
COPY --from=builder /bin/api-server /bin/api-server

# Expose service port and set entrypoint
EXPOSE 8080
ENTRYPOINT ["/bin/api-server"]
The Result: A 12 MB production image with zero unnecessary operating system files, zero vulnerabilities, and instant startup under 10 milliseconds!

4. Production Next.js Standalone Multi-Stage Build

By enabling output: 'standalone' in next.config.js, Next.js automatically traces your imports and copies only required node_modules into a minimal bundle:

# Stage 1: Install all dependencies (including devDependencies for build)
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Stage 2: Build Next.js application
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

# Stage 3: Minimal Production Runner Stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000

# Create dedicated unprivileged runtime user
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs

# Copy static assets and traced standalone bundle
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]

Next Up: Container Security Practices

In Chapter 15, explore non-root users, dropping Linux capabilities, read-only root filesystems, and vulnerability scanning.

Chapter 15: Container Security Practices →

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools