Containers & Deployment

Docker & Deployment

Docker verpackt deine App und ihre Umgebung in ein Image, das überall gleich läuft. Multi-stage builds, Caching und CI/CD machen das Deployment zu einem wiederholbaren Prozess.

intermediate14 min readUpdated 15. Sept. 2026
Dockerfile
dockerfile
// Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Image
Eine schreibgeschützte Vorlage
Container
Ein laufendes Image
Build-Datei
Dockerfile
Layers
Gecacht und wiederverwendet
Compose
Multi-Service-Apps
Registry
Speicherort für Images

Warum es wichtig ist

Warum Container das Deployment revolutioniert haben

Überall konsistent

Das Image enthält das OS, die Runtime und alle Abhängigkeiten, sodass es auf deinem Laptop, in der CI und in der Produktion identisch läuft.

Standardmäßig isoliert

Container teilen sich den Host-Kernel, isolieren aber Prozesse, Dateisysteme und Netzwerke, was den Blast Radius begrenzt.

Portabel und skalierbar

Das gleiche Image läuft auf jedem Container-Host, was das Skalieren und den Wechsel zwischen Clouds vereinfacht.

Das Gesamtbild

Die drei Kernkonzepte von Docker

Ein Image ist der Build, ein Container ist der Run und eine Registry ist der Weg, wie Images zwischen Maschinen transportiert werden.

Das Image

Build

Eine geschichtete, schreibgeschützte Vorlage, die aus einem Dockerfile erstellt wird.

Der Container

Run

Eine laufende Instanz eines Images mit eigenem Prozess und Dateisystem.

Die Registry

Distribute

Ein Speicher für Images, aus dem CI und Produktion ziehen.

Docker auf einen Blick

Das Herzstück von Docker

Dockerfile

Anweisungen, die ein Image Layer für Layer aufbauen.

Layers und Cache

Jede Anweisung ist ein gecachter Layer, sodass unveränderte Schritte wiederverwendet werden.

Multi-stage builds

In einer Stage bauen und nur das Ergebnis in einem kleineren finalen Image ausliefern.

Compose

Definition und Ausführung von Multi-Container-Apps, wie z. B. eine App plus Datenbank.

Registry

Images zu Docker Hub, GHCR oder einer privaten Registry pushen und pullen.

CI/CD

Automatisch bei jeder Änderung bauen, testen und deployen.

Eine kurze Geschichte

Von VMs über Container zu CI/CD

  1. 2013

    Docker Release

    Container werden für normale Entwickler zugänglich.

    13
  2. 2015

    Compose und Orchestrierung

    Compose und Kubernetes machen Multi-Service-Deployments praktikabel.

    15
  3. 2017

    Multi-stage builds

    Kleinere Produktions-Images werden zum Standard.

    17
  4. 2020

    Container überall

    Container-basierte CI/CD und Serverless-Container-Plattformen werden zum Mainstream.

    20
  5. Heute

    Die Deployment-Baseline

    Die meisten Produktionssysteme werden als Container-Images ausgeliefert.

    Heute

Der vollständige Leitfaden

Docker & Deployment: Alles was Sie wissen müssen

Warum Container gewonnen haben

Vor Containern bedeutete Deployment, eine Umgebung exakt zu reproduzieren: die richtige Runtime-Version, die passenden System-Libraries, die korrekte Konfiguration. „Auf meinem Rechner funktioniert es“ war ein echtes Problem, und Server wichen im Laufe der Zeit immer weiter von der Entwicklungsumgebung ab.

Docker löste dieses Problem, indem die Anwendung und ihre Umgebung in ein einziges Image verpackt wurden. Das Image läuft auf einem Laptop, in der CI und in der Produktion identisch, da es alles Notwendige mitbringt. Diese Konsistenz ist der Grund, warum Container zum Standard für Deployments wurden und warum das Erlernen von Docker eine der wertvollsten Fähigkeiten für das Ausliefern von Software ist.

Images und Container

Ein Image ist eine schreibgeschützte Vorlage, die aus einem Dockerfile erstellt wird und aus gecachten Layern besteht. Ein Container ist eine laufende Instanz eines Images mit eigenem beschreibbarem Layer, Prozess und Netzwerk. Man baut einmal und startet viele Instanzen.

docker build -t my-app:1.0 .
docker run -p 3000:3000 my-app:1.0
docker ps
docker logs <container>

Das -p Flag mappt einen Host-Port auf einen Container-Port. Container sind per Design ephemeral: Sie können diese beliebig stoppen, entfernen und neu erstellen, da das Image die „Source of Truth“ ist.

Das Dockerfile

Das Dockerfile ist das Rezept für den Build-Prozess. Jede Anweisung erstellt einen Layer.

# Dockerfile
FROM node:22-alpine
WORKDIR /app

# install dependencies first for better caching
COPY package*.json ./
RUN npm ci --omit=dev

# then copy source
COPY . .

ENV NODE_ENV=production
EXPOSE 3000
USER node
CMD ["node", "server.js"]

Die Reihenfolge ist entscheidend. Wenn die Dependency-Manifeste kopiert und die Installation durchgeführt wird, bevor der Quellcode kopiert wird, führt eine Code-Änderung nur dazu, dass der kostengünstige Source-Layer ungültig wird und nicht die aufwendige Installation. npm ci sorgt für eine reproduzierbare Installation und USER node stellt sicher, dass der Prozess als Non-Root-User ausgeführt wird.

Layer-Caching

Docker cached jeden Layer und verwendet ihn wieder, wenn die Inputs unverändert sind. Deshalb ist die oben genannte Reihenfolge bewusst gewählt: Platzieren Sie Dinge, die sich selten ändern, oben und Dinge, die sich häufig ändern, unten.

# cached unless package files change
COPY package*.json ./
RUN npm ci

# invalidated on every source change
COPY . .

Eine .dockerignore-Datei ist ebenfalls wichtig. Ohne sie werden node_modules, .git und Build-Outputs in den Build-Kontext kopiert, was den Build verlangsamt und dazu führen kann, dass Dateien in das Image gelangen.

node_modules
.git
dist
.env

Multi-stage Builds

Ein Multi-stage Build kompiliert in einem Image und liefert nur das Ergebnis in einem anderen aus.

# Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Das finale Image enthält nur die gebauten statischen Dateien und einen Webserver, nicht aber die Node-Runtime, den Quellcode oder die Build-Tools. Das Ergebnis ist kleiner, lässt sich schneller pullen und bietet eine wesentlich geringere Angriffsfläche. Für eine Server-App wäre die finale Stage ein schlankes Node-Image, das nur die Production-Dependencies enthält.

Compose für Multi-Service-Apps

Echte Anwendungen benötigen in der Regel mehr als einen Prozess. Compose definiert diese gemeinsam.

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://db:5432/app
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - data:/var/lib/postgresql/data

volumes:
  data:

depends_on steuert die Startreihenfolge, volumes sorgt für die Persistenz von Daten über Container-Neustarts hinweg, und Services erreichen sich gegenseitig über ihren Namen im gemeinsamen Netzwerk. Compose ist ideal für die lokale Entwicklung und einfache Single-Host-Deployments.

Registries und CI/CD

Images durchlaufen eine Registry, wie zum Beispiel Docker Hub, GitHub Container Registry oder eine private Registry. Eine typische Pipeline sieht so aus:

  1. Abhängigkeiten installieren und Tests ausführen.
  2. Das Image bauen und mit dem Commit-SHA taggen.
  3. Das Image in die Registry pushen.
  4. Den neuen Tag in der Zielumgebung deployen.
docker build -t ghcr.io/me/app:$GIT_SHA .
docker push ghcr.io/me/app:$GIT_SHA

Da das Image unveränderlich (immutable) ist und über den Commit getaggt wird, bestehen Rollbacks lediglich darin, einen vorherigen Tag zu deployen. Dies ist der Kern eines zuverlässigen Deployment-Prozesses: Einmal bauen, dasselbe Artefakt durch die verschiedenen Umgebungen befördern und niemals erneut für die Produktion bauen.

Best Practices für die Produktion

  • Health Checks. Stellen Sie einen Endpoint bereit, den die Plattform abfragen kann, um zu prüfen, ob der Container bereit ist.
  • Nicht-Root-Benutzer. Führen Sie den Prozess mit einem Benutzer aus, der nur die minimal erforderlichen Berechtigungen besitzt.
  • Schreibgeschütztes Dateisystem, wo immer möglich, mit explizit definierten beschreibbaren Volumes.
  • Graceful Shutdown. Verarbeiten Sie SIGTERM, um laufende Anfragen ordnungsgemäß zu beenden.
  • Ressourcenlimits. Legen Sie CPU- und Memory-Limits fest, damit ein einzelner Container nicht alle Ressourcen beansprucht.
  • Base-Image-Versionen fixieren (Pinning) für reproduzierbare Builds.
  • Secrets zur Laufzeit bereitstellen, niemals direkt in das Image einbetten.
  • Kleine Base-Images wie Alpine oder distroless verwenden, um die Anzahl potenzieller Schwachstellen zu reduzieren.

Best Practices

  • Ordnen Sie die Dockerfile-Instruktionen so an, dass die am seltensten geänderten zuerst kommen.
  • Nutzen Sie Multi-Stage Builds und liefern Sie nur das aus, was wirklich benötigt wird.
  • Fügen Sie ein .dockerignore für node_modules, .git, dist und .env hinzu.
  • Führen Sie Prozesse als Non-Root-User aus.
  • Taggen Sie Images mit dem Commit-SHA und nicht nur mit latest.
  • Bauen Sie das Image einmal und befördern Sie es durch die verschiedenen Umgebungen.
  • Behandeln Sie SIGTERM für ein Graceful Shutdown.

Häufige Fehler

  • Kopieren von node_modules in das Image.
  • Ausführen als root.
  • Einbetten von Secrets in das Image oder Committen von .env.
  • Verwendung von latest als einzigem Tag, wodurch die Reproduzierbarkeit verloren geht.
  • Unterschiedliches Neu-Builden des Images für jede Umgebung.
  • Ignorieren von .dockerignore, was zu riesigen und langsamen Builds führt.

Wie geht es weiter?

Container machen das Deployment vorhersagbar, und die gleichen Fähigkeiten kommen zum Einsatz, egal ob Sie an eine VM, Kubernetes oder eine serverless Container-Plattform liefern. Vertiefen Sie Ihr Wissen über die Runtime im Node.js-Guide, verwalten Sie Abhängigkeiten mit npm, stellen Sie die Anwendung über HTTP bereit und sichern Sie diese mit Web Security ab. Containerisieren Sie anschließend eine kleine App und führen Sie ein vollständiges End-to-End-Deployment durch.

Image erstellen

Ein Multi-stage build hält Build-Tools aus dem finalen Image fern, was es kleiner macht und die Angriffsfläche reduziert.

Bevorzugt
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
Vermeiden
FROM node:22
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/server.js"]
# ships dev deps, source
# and build tools

Als User ausführen

Führe den Container als Non-Root-User aus, damit ein Kompromiss nicht einfach auf den Host eskalieren kann.

Bevorzugt
FROM node:22-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]
Vermeiden
FROM node:22-alpine
WORKDIR /app
COPY . .
# runs as root by default
CMD ["node", "server.js"]

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Docker & Deployment zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Docker & Deployment — mit Quizzen und echtem Code, den Sie im Browser ausführen können.