Ein mehrstufiger Build (multi-stage build) ist ein Verfahren, bei dem ein einziges Dockerfile mehrere Bauphasen enthält. Die erste Phase bringt eine vollständige Werkzeugkette mit und erzeugt das Ergebnis; die letzte Phase beginnt mit einem minimalen Basisimage und übernimmt nur noch die fertigen Artefakte.
Das Problem, das es löst
Wer Compiler, Paketmanager und Quellcode im Produktionsimage lässt, transportiert unnötige Software mit: Das Image ist groß, das Ziehen dauert, und jede zusätzliche Bibliothek ist eine mögliche Schwachstelle. Der klassische Ausweg über mehrere Dockerfiles und Skripte war fehleranfällig. Mit mehreren FROM-Anweisungen in einer Datei genügt ein einziger Build-Aufruf.
Muster
# syntax=docker/dockerfile:1
FROM node:22-bookworm-slim AS deps
WORKDIR /build
COPY package.json package-lock.json ./
RUN npm ci
FROM deps AS build
COPY . .
RUN npm run build
FROM deps AS test
RUN npm test
FROM node:22-bookworm-slim AS runtime
WORKDIR /app
COPY --from=build /build/dist ./dist
COPY --from=deps /build/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]
Der Bau wird mit --target auf eine bestimmte Phase gelenkt: docker build --target test . lässt den Build fehlschlagen, wenn Tests scheitern, und erzeugt kein Produktionsimage. Das ist ein sehr wirksames, einfaches Qualitätstor in der CI/CD-Pipeline.
Verwendete Mechanismen
- COPY --from=phase: kopiert Dateien aus einer früheren Phase – die entscheidende Anweisung. Explizite Pfade wählen;
COPY --from=build / /schleppt die gesamte Bauumgebung ein. - Gemeinsame Basisphase: Eine Phase
basemit den installierten Laufzeitbibliotheken lässt sich von mehreren Zielphasen erben. - BuildKit-Cachemounts:
RUN --mount=type=cache,target=/root/.cachehält Paketmanager-Caches zwischen Builds und beschleunigt Läufe erheblich. - Geheimnisse:
RUN --mount=type=secret,id=TOKENliest Zugangsdaten zur Bauzeit, ohne sie in einer Schicht zu hinterlassen. - Plattformübergreifend: Mit
--platform=linux/arm64und Build-Argumenten lässt sich für fremde Architekturen bauen.
Minimale Laufzeitphase
Die letzte Phase sollte so leer wie möglich sein. Für statisch gelinkte Binaries eignen sich FROM scratch oder Distroless-Images: kein Shell, kein Paketmanager, kein Debug-Werkzeug. Für dynamisch gelinkte Programme braucht man die passende libc – ein gegen glibc gebautes Binary läuft nicht auf einem musl/alpine-Image. Diese Falle ist klassisch und zeigt sich erst zur Laufzeit mit einem kryptischen Ladefehler; der Bau selbst läuft durch, weil COPY nichts prüft.
Verwandte Grundlagen: Dockerfile, Image-Layer, Docker-Image, Buildah.