Docker ist 2026 die Standardtechnologie für Software-Deployment — kein ernsthaftes Team deployt noch direkt auf bare metal. Doch produktionsreife Docker-Konfigurationen zu schreiben ist aufwändig: Layer-Caching richtig nutzen, Multi-Stage Builds für minimale Image-Größen, Secrets sicher verwalten, Security-Hardening korrekt umsetzen. Claude Code übernimmt genau das — und generiert vollständige, sofort deploybare Dockerfiles und Compose-Setups, die Best Practices von 2026 bereits eingebaut haben.

Dieser Artikel zeigt, wie du Docker mit Claude Code effektiv einsetzt — von einfachen Dockerfiles bis zu Multi-Plattform-Builds mit BuildKit und GitHub Actions.

~80%
kleinere Images durch Multi-Stage
3x
schnellere Builds mit Layer-Cache
0
Root-Prozesse bei Hardening
95%
weniger Dockerfile-Fehler mit AI

Inhalt

  1. Dockerfile Grundlagen & Layer-Caching
  2. Multi-Stage Builds
  3. Docker Compose
  4. Volumes & Networking
  5. Security-Hardening
  6. CI/CD & Registry

1. Dockerfile Grundlagen & Layer-Caching-Strategie

Ein Dockerfile beschreibt Schritt für Schritt, wie ein Container-Image aufgebaut wird. Jede Anweisung erzeugt einen Layer — und Docker cached diese Layer intelligent. Wer die Reihenfolge falsch wählt, baut bei jeder Codeänderung das komplette Image neu. Claude Code kennt die optimale Reihenfolge und setzt sie automatisch um.

⚡ Layer-Caching-Grundregel Dependency-Installation IMMER vor dem Kopieren des eigentlichen Source-Codes. Nur so wird der teuerste Schritt (npm install, pip install) gecached und nur bei Änderungen in package.json wiederholt.
Dockerfile

Node.js App — Layer-Caching optimal genutzt

Dockerfile
# ────────────────────────────────────────────────────── # Node.js 22 LTS — Optimiertes Layer-Caching # Generiert mit Claude Code — claude-code-docker-2026 # ────────────────────────────────────────────────────── # 1. Base Image — LTS-Version, Alpine für kleine Footprint FROM node:22-alpine AS base # 2. Working Directory setzen WORKDIR /app # 3. System-Dependencies (nur wenn nötig — wird gecached!) RUN apk add --no-cache \ dumb-init \ curl \ && rm -rf /var/cache/apk/* # 4. Dependency-Dateien zuerst kopieren (vor dem Code!) # Nur wenn package.json sich ändert → npm ci wird re-executed COPY package.json package-lock.json ./ # 5. Dependencies installieren (teuerster Schritt — gecached!) RUN npm ci --omit=dev --ignore-scripts \ && npm cache clean --force # 6. Source-Code kopieren (ändert sich oft — kommt NACH deps!) COPY src/ ./src/ COPY public/ ./public/ # 7. Build-Step (TypeScript kompilieren, Assets bundlen) RUN npm run build # 8. Umgebungsvariablen ENV NODE_ENV=production \ PORT=3000 \ LOG_LEVEL=info # 9. Port deklarieren (Dokumentation + Docker-Networking) EXPOSE 3000 # 10. Health-Check einbauen HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 # 11. Non-root User anlegen und wechseln RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser # 12. dumb-init als PID 1 — korrektes Signal-Handling ENTRYPOINT ["/usr/bin/dumb-init", "--"] CMD ["node", "src/server.js"]

CMD vs. ENTRYPOINT — die wichtige Unterscheidung

ENTRYPOINT definiert den Prozess, der immer läuft — er kann beim Start nicht einfach überschrieben werden. CMD liefert Standard-Argumente, die beim docker run leicht ersetzt werden können. Die Kombination aus beiden, wie oben gezeigt, ist Production Best Practice: dumb-init als stabiler PID-1-Prozess, der Node.js korrekt startet und Signale weiterleitet.

AnweisungZweckÜberschreibbar?Wann nutzen?
CMDStandard-Befehl/Argumente✅ Ja (docker run)Flexible Apps, Dev-Tooling
ENTRYPOINTHaupt-Prozess (fest)⚠ Nur mit --entrypointServices, daemons
ENTRYPOINT + CMDFester Prozess + flexible Argumente✅ Args überschreibbarProduction Services

2. Multi-Stage Builds: Minimale Images für Production

Multi-Stage Builds sind eine der mächtigsten Docker-Techniken: Du verwendest ein fettes Build-Image mit allen Compiler-Tools, und kopierst am Ende nur das fertige Artefakt in ein minimales Production-Image. Das Ergebnis: Images, die 80–95% kleiner sind als naive Single-Stage Builds.

✅ Warum Multi-Stage Builds 2026 Pflicht sind Kleinere Images bedeuten schnellere Pulls, weniger Angriffsfläche, niedrigere Registry-Kosten und kürzere Pod-Startup-Zeiten in Kubernetes. Claude Code generiert Multi-Stage Dockerfiles auf Anfrage vollständig korrekt.
Multi-Stage

Python FastAPI — Builder → Distroless Production

Dockerfile
# ────────────────────────────────────────────────────── # Multi-Stage Build: Python FastAPI → Distroless # Stage 1: Builder (mit pip, gcc, alle Build-Tools) # Stage 2: Production (nur Python + App, kein Build-Tooling) # ────────────────────────────────────────────────────── # ── STAGE 1: BUILDER ────────────────────────────────── FROM python:3.12-slim AS builder WORKDIR /build # Build-Dependencies (gcc für C-Extensions wie pydantic-core) RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libpq-dev \ && rm -rf /var/lib/apt/lists/* # Virtuelle Umgebung anlegen (isoliert, leicht kopierbar) RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" # Requirements zuerst (Layer-Cache!) COPY requirements.txt . RUN pip install --no-cache-dir --upgrade pip \ && pip install --no-cache-dir -r requirements.txt # ── STAGE 2: PRODUCTION ─────────────────────────────── FROM python:3.12-slim AS production # Nur Runtime-Dependencies (kein gcc, kein pip, etc.) RUN apt-get update && apt-get install -y --no-install-recommends \ libpq5 \ curl \ && rm -rf /var/lib/apt/lists/* \ && groupadd --system app && useradd --system --gid app app # Nur die fertige venv aus Stage 1 kopieren COPY --from=builder /opt/venv /opt/venv WORKDIR /app COPY app/ ./app/ COPY alembic.ini ./ ENV PATH="/opt/venv/bin:$PATH" \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONFAULTHANDLER=1 EXPOSE 8000 HEALTHCHECK --interval=20s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 USER app CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Multi-Stage

Go Service — Builder → Scratch (kleinstmögliches Image)

Dockerfile
# Go statisches Binary → scratch (kein OS, nichts) # Finales Image: ~10-15 MB statt ~800 MB mit golang:1.22 FROM golang:1.22-alpine AS builder WORKDIR /src # Go Module Cache (wird gecached wenn go.mod/go.sum unverändert) COPY go.mod go.sum ./ RUN go mod download && go mod verify COPY . . # Statisches Binary bauen (CGO disabled = keine libc-Abhängigkeit) RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build \ -ldflags="-w -s -X main.version=$(git describe --tags --dirty)" \ -trimpath \ -o /out/app ./cmd/server # ── PRODUCTION: scratch ─────────────────────────────── FROM scratch # CA-Zertifikate aus Builder kopieren (für HTTPS-Calls) COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ # Nur das fertige Binary COPY --from=builder /out/app /app EXPOSE 8080 ENTRYPOINT ["/app"]

Image-Größen im Vergleich

Base ImageTypGröße (ca.)Empfehlung
node:22Standard Debian~1.1 GB❌ Nur lokal/Dev
node:22-slimMinimales Debian~230 MB⚠ Akzeptabel
node:22-alpineAlpine Linux~60 MB✅ Empfohlen
golang → scratchKein OS~10 MB✅ Ideal für Go
python:3.12-slimMinimales Debian~150 MB✅ Für Python

3. Docker Compose: Vollständige Dev- und Prod-Umgebungen

Docker Compose orchestriert mehrere Container als zusammenhängende Applikation. Mit Compose V2 (integriert in Docker CLI als docker compose) und Features wie profiles, depends_on mit Health-Checks und docker compose watch ist 2026 ein vollständiger lokaler Dev-Stack in wenigen Zeilen YAML definiert.

Compose

Fullstack App: API + PostgreSQL + Redis + Nginx

docker-compose.yml
# docker-compose.yml — Fullstack Dev + Production Stack # Claude Code generiert: Healthchecks, Profiles, Watch-Mode name: myapp services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${DB_NAME:-myapp} POSTGRES_USER: ${DB_USER:-postgres} POSTGRES_PASSWORD: ${DB_PASS:?DB_PASS required} PGDATA: /var/lib/postgresql/data/pgdata volumes: - postgres_data:/var/lib/postgresql/data - ./db/init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-myapp}"] interval: 10s timeout: 5s retries: 5 start_period: 20s networks: - backend profiles: [dev, prod, full] redis: image: redis:7-alpine restart: unless-stopped command: redis-server --requirepass ${REDIS_PASS:?REDIS_PASS required} --save 60 1000 --loglevel warning volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "--pass", "${REDIS_PASS}", "ping"] interval: 10s timeout: 3s retries: 3 networks: - backend profiles: [dev, prod, full] api: build: context: . dockerfile: Dockerfile target: production cache_from: - type=registry,ref=ghcr.io/myorg/myapp:cache restart: unless-stopped env_file: .env environment: DATABASE_URL: postgresql://${DB_USER}:${DB_PASS}@postgres:5432/${DB_NAME} REDIS_URL: redis://:${REDIS_PASS}@redis:6379 depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 20s timeout: 5s retries: 3 networks: - backend - frontend profiles: [dev, prod, full] # Live-Reload im Dev-Modus (docker compose watch) develop: watch: - action: sync path: ./src target: /app/src - action: rebuild path: package.json nginx: image: nginx:1.27-alpine restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/certs:/etc/nginx/certs:ro - static_files:/var/www/static:ro depends_on: api: condition: service_healthy networks: - frontend profiles: [prod, full] networks: backend: driver: bridge internal: true # kein direkter Internet-Zugriff! frontend: driver: bridge volumes: postgres_data: driver: local redis_data: driver: local static_files: driver: local

Profiles — dev, prod und testing getrennt starten

Mit profiles startest du nur die Services, die du gerade brauchst:

Bash
# Nur Dev-Stack (ohne Nginx/SSL) docker compose --profile dev up -d # Vollständiger Prod-Stack docker compose --profile prod up -d # Live-Reload für Entwicklung docker compose --profile dev watch # Logs verfolgen docker compose logs -f api postgres

4. Volumes & Networking

Persistent Storage und Netzwerk-Isolation sind zwei Kernaspekte eines sicheren Container-Deployments. Claude Code erklärt nicht nur die Konzepte — es generiert vollständige Konfigurationen, die auf Anhieb funktionieren.

Networking

Volume-Typen im Überblick

docker-compose.yml — Volumes
volumes: # Named Volume: Docker verwaltet Speicherort # ✅ Empfohlen für Datenbanken, persistente App-Daten postgres_data: driver: local driver_opts: type: none o: bind device: /mnt/ssd/postgres # Auf SSD binden # tmpfs Volume: nur im RAM — verschwindet beim Stopp tmp_cache: driver_opts: type: tmpfs device: tmpfs o: size=512m,uid=1000 services: api: volumes: # Named Volume (persistent) - postgres_data:/var/lib/postgresql/data # Bind Mount (Host-Datei in Container) - ./config/app.yaml:/app/config.yaml:ro # tmpfs Mount (RAM-basiert, kein I/O-Overhead) - type: tmpfs target: /tmp tmpfs: size: 134217728 # 128 MB
Networking

Netzwerk-Isolation — backend internal, frontend exposed

docker-compose.yml — Networks
networks: # Backend: kein Internet-Zugriff von außen backend: driver: bridge internal: true # ← Schlüssel: kein Routing nach außen ipam: driver: default config: - subnet: 172.20.0.0/24 # Frontend: Nginx ↔ Welt frontend: driver: bridge # Overlay für Docker Swarm / Multi-Host swarm_net: driver: overlay attachable: true driver_opts: encrypted: "true" # Traffic zwischen Nodes verschlüsselt services: postgres: networks: - backend # Nur API kann DB erreichen, nie Nginx direkt! api: networks: - backend # DB und Redis - frontend # Nginx → API nginx: networks: - frontend # Nur Nginx ist öffentlich erreichbar

DNS Resolution in Docker Networks

Docker hat einen eingebauten DNS-Server: Container können sich innerhalb eines Netzwerks über ihren Service-Namen erreichen. postgres ist also direkt unter postgres:5432 erreichbar — keine IP-Adressen nötig. Service-Aliases ermöglichen mehrere DNS-Namen für einen Container.

Bash — Networking debuggen
# DNS innerhalb eines Containers prüfen docker exec -it myapp-api-1 nslookup postgres docker exec -it myapp-api-1 curl -v http://postgres:5432 # Netzwerke auflisten + inspizieren docker network ls docker network inspect myapp_backend # Container direkt verbinden (ohne Compose) docker network connect myapp_backend my-debug-container

5. Security-Hardening: Container-Sicherheit 2026

Standardmäßig laufen Docker-Container als root — das ist ein erhebliches Sicherheitsrisiko. 2026 sind Security-Best-Practices für Container nicht optional, sondern Pflicht. Claude Code kennt alle relevanten Maßnahmen und implementiert sie auf Anfrage vollständig.

🚨 Container als root = Sicherheitsrisiko Läuft ein Prozess im Container als root und gibt es einen Container-Escape, hat der Angreifer root auf dem Host. Non-root User, read-only Filesystem und Capability-Drops sind Pflicht.
Security

Gehärtetes Dockerfile mit Non-root, Readonly FS, Capabilities

Dockerfile — Security-Hardened
FROM node:22-alpine AS base # ── Security Layer 1: Non-root User ───────────────── RUN addgroup -S -g 10001 appgroup \ && adduser -S -u 10001 -G appgroup -h /app -s /sbin/nologin appuser WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev && npm cache clean --force COPY --chown=appuser:appgroup src/ ./src/ # ── Security Layer 2: Verzeichnisrechte ────────────── RUN chown -R appuser:appgroup /app \ && chmod -R 550 /app/src \ && mkdir -p /app/tmp && chown appuser:appgroup /app/tmp USER 10001:10001 # UID statt Username (sicherer in K8s) ENV NODE_ENV=production EXPOSE 3000 # Kein root-Prozess als PID 1 ENTRYPOINT ["/usr/bin/dumb-init", "--"] CMD ["node", "--max-old-space-size=512", "src/server.js"]
Security

docker-compose.yml Security-Optionen

docker-compose.yml
services: api: build: . user: "10001:10001" # Read-only Filesystem read_only: true tmpfs: - /tmp:size=64m,mode=1777 - /run:size=16m # Alle Capabilities droppen, nur nötige erlauben cap_drop: - ALL cap_add: - NET_BIND_SERVICE # Nur wenn Port <1024 # Seccomp-Profil security_opt: - no-new-privileges:true - seccomp:./seccomp-profile.json # Resource-Limits deploy: resources: limits: cpus: "1.0" memory: 512M reservations: memory: 128M # Secrets (nicht als Env-Variablen!) secrets: - db_password - api_key secrets: db_password: file: ./secrets/db_password.txt api_key: environment: API_KEY

Trivy: Container-Scanning im CI/CD

Trivy ist der Standard-Scanner für Container-Images und scannt auf CVEs, Fehlkonfigurationen und Secrets. Claude Code generiert die GitHub-Actions-Integration auf Anfrage:

Bash
# Lokales Scanning trivy image --severity HIGH,CRITICAL myapp:latest # SBOM generieren (Software Bill of Materials) trivy image --format cyclonedx --output sbom.json myapp:latest # Filesystem-Scan (ohne Build) trivy fs --scanners vuln,secret,config .

.dockerignore — was niemals ins Image gehört

.dockerignore
# Secrets und lokale Konfigurationen .env .env.* *.pem *.key secrets/ # Development-Artefakte node_modules/ __pycache__/ *.pyc .pytest_cache/ .mypy_cache/ # CI/CD und Git .git/ .github/ .gitlab-ci.yml .travis.yml # Dokumentation und Tests (nicht im Prod-Image) docs/ tests/ *.test.ts *.spec.js coverage/ # IDE-Dateien .vscode/ .idea/ *.swp

6. CI/CD & Registry: GitHub Actions + BuildKit + GHCR

Ein produktionsreifer Docker-Workflow baut Images nicht manuell — der gesamte Build-, Test- und Push-Prozess läuft automatisch im CI/CD. Mit BuildKit, Multi-Platform Builds (AMD64 + ARM64) und dem GitHub Container Registry (GHCR) hat Claude Code die vollständige Pipeline parat.

CI/CD

GitHub Actions: Build, Scan, Push — vollständige Pipeline

.github/workflows/docker.yml
name: Docker Build & Push on: push: branches: [main] tags: ['v*.*.*'] pull_request: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: build-and-push: runs-on: ubuntu-latest permissions: contents: read packages: write security-events: write # Für Trivy SARIF-Upload steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU (für ARM64 Builds) uses: docker/setup-qemu-action@v3 - name: Set up Docker BuildKit uses: docker/setup-buildx-action@v3 with: driver-opts: image=moby/buildkit:latest - name: Login to GHCR if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=ref,event=branch type=sha,prefix=sha-,format=short - name: Build and push (Multi-Platform) id: build uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max target: production build-args: | BUILD_VERSION=${{ github.sha }} BUILD_DATE=${{ github.event.head_commit.timestamp }} - name: Scan Image with Trivy uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} format: sarif output: trivy-results.sarif severity: CRITICAL,HIGH exit-code: '1' # Build fehlschlagen bei CVE! - name: Upload Trivy SARIF uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: trivy-results.sarif

BuildKit Cache optimieren — maximale Build-Geschwindigkeit

Dockerfile — BuildKit Cache Mounts
# BuildKit Cache Mounts: npm/pip cache zwischen Builds teilen # Diese Caches werden NICHT ins Image gebaut — nur im Build-Prozess genutzt # Python pip Cache RUN --mount=type=cache,target=/root/.cache/pip \ pip install --no-cache-dir -r requirements.txt # npm Cache RUN --mount=type=cache,target=/root/.npm \ npm ci --prefer-offline # Go Build Cache RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ go build -o /out/app ./cmd/server # apt-get Cache RUN --mount=type=cache,target=/var/cache/apt \ --mount=type=cache,target=/var/lib/apt \ apt-get update && apt-get install -y --no-install-recommends libpq5
CI/CD

Image-Tagging-Strategie für Produktion

Bash
# Manueller Build mit BuildKit + Multi-Platform export DOCKER_BUILDKIT=1 docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ghcr.io/myorg/myapp:v1.2.3 \ --tag ghcr.io/myorg/myapp:latest \ --tag ghcr.io/myorg/myapp:sha-$(git rev-parse --short HEAD) \ --cache-from type=registry,ref=ghcr.io/myorg/myapp:cache \ --cache-to type=registry,ref=ghcr.io/myorg/myapp:cache,mode=max \ --target production \ --push \ . # Rollback auf vorherige Version docker pull ghcr.io/myorg/myapp:sha-abc1234 docker tag ghcr.io/myorg/myapp:sha-abc1234 ghcr.io/myorg/myapp:latest docker push ghcr.io/myorg/myapp:latest

Claude Code und Docker: Praxistipps

Claude Code kann nicht nur Dockerfiles generieren — es analysiert bestehende Konfigurationen, findet Sicherheitslücken und erklärt komplexe Netzwerk-Topologien. Typische Prompts, die besonders gut funktionieren:

Prompt-Beispiele
✅ Best Practice: Iterativ mit Claude Code arbeiten Starte mit einem einfachen Prompt ("Erstelle ein Dockerfile für meine Express-App"), dann verfeinere schrittweise: "Füge Multi-Stage Build hinzu", "Härte den Container gegen Root-Exploits", "Optimiere für GitHub Actions CI". Claude Code merkt sich den Kontext und verbessert inkrementell.

Production-Ready Docker Checklist

Produktionsreife Docker-Configs in Sekunden?

Claude Code generiert vollständige Dockerfiles, Compose-Setups und CI/CD-Pipelines — optimiert, gehärtet und sofort deploybar. Jetzt kostenlos testen.

Kostenlos starten →