Lokale Entwicklung mit Docker klingt für viele zunächst nach langwieriger Einarbeitung und komplexen Konfigurationen. In meiner Praxis habe ich jedoch Standard-Workflows entwickelt, mit denen man eine kleine Webapp in unter einer Stunde containerisieren kann — inklusive Dockerfile, Docker Compose, Live-Reload und einigen nützlichen Tipps für Debugging und Sicherheit. In diesem Artikel erkläre ich Schritt für Schritt, wie ich vorgehe, welche Fallen es gibt und welche Entscheidungen sich in der Praxis bewährt haben.

Warum Container für die lokale Entwicklung?

Für mich ist der größte Vorteil von Docker die Konsistenz zwischen Entwicklungs- und Produktionsumgebung. Keine „bei mir läuft's“-Probleme mehr wegen unterschiedlicher Node-/Python-Versionen, Datenbank-Konfiguration oder fehlender Abhängigkeiten. Außerdem erlaubt Docker schnelle Isolation: Ich kann mehrere Projekte gleichzeitig laufen lassen, ohne dass sich Paket- oder Port-Konfigurationen in die Quere kommen.

Was du brauchst — Voraussetzungen

Bevor wir starten, solltest du folgendes installiert haben:

  • Docker Desktop (Windows/Mac) oder Docker Engine + Docker Compose (Linux)
  • Ein Terminal (bash, zsh, PowerShell)
  • Dein Webapp-Repository (z. B. Node/Express, Flask oder ein statisches Frontend)
  • Wenn du noch kein Projekt hast, erstelle ich meistens ein kleines Node/Express-Template — das ist schnell aufgesetzt und eignet sich perfekt fürs Demonstrieren.

    Plan: In unter einer Stunde containerisieren

    Mein Standardplan hat vier Schritte, die ich nacheinander abarbeite:

  • Lokale App-Struktur prüfen und .dockerignore erstellen
  • Dockerfile schreiben (idealerweise multi-stage für kleinere Images)
  • docker-compose.yml hinzufügen für Services, Volumes und Netzwerke
  • Build, Run, Debug: Container starten und Live-Reload sicherstellen
  • Schritt 1 — Project cleanup und .dockerignore

    Bevor du ein Dockerfile schreibst, räume kurz deine Projektstruktur auf. Dateiarten, die im Image nichts zu suchen haben (z. B. node_modules, .git, lokale Logs), kommen in eine .dockerignore. Das spart Build-Zeit und reduziert Image-Größe.

    Beispielzeilen für .dockerignore:

  • node_modules
  • .git
  • dist
  • .env
  • Wichtig: .env nicht ins Image packen — Umgebungsvariablen gehören zur Laufzeit (docker-compose oder Secrets).

    Schritt 2 — Das Dockerfile (ein pragmatisches Beispiel)

    Ich nutze gern Multi-Stage-Builds bei Node- oder React-Apps: Build im ersten Stage, schlankes Runtime-Image im zweiten. Für eine Node/Express-App etwa so (Kurzform in Text):

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

    Im zweiten Stage nur das Nötigste:

    FROM node:18-alpine
    WORKDIR /app
    COPY --from=build /app .
    ENV NODE_ENV=production
    EXPOSE 3000
    CMD [\"node\", \"dist/index.js\"]

    Für einfache APIs ohne Build-Schritt kannst du den Build-Stage weglassen. Bei Python/Flask analog: base image python:3.11-slim, pip install -r requirements.txt, COPY app, CMD flask run --host=0.0.0.0.

    Schritt 3 — docker-compose.yml: Mehrere Services einfach verwalten

    docker-compose ist in der lokalen Entwicklung fast unverzichtbar: Datenbank, Redis, Search-Index und die App in einem Netzwerk starten. Ein typisches compose-Setup:

  • app: build, ports (z. B. "3000:3000"), volumes (für Live-Reload), environment
  • db: image postgres, volumes für Persistenz, environment für Passwort/DB
  • optional: adminer/phpmyadmin für DB-UI
  • Wichtig: Für Live-Reload benutze ich ein Bind-Mount (lokaler Code in den Container). Beispiel:

    volumes:
    - ./:/app

    Das erlaubt, dass Änderungen am Host sofort im Container sichtbar sind. Beachte: Bei multi-stage Builds kopierst du normalerweise die gebauten Artefakte ins Image — für die Entwicklung kannst du statt dessen den Source als Volume mounten und den Dev-Server im Container laufen lassen (z. B. nodemon).

    Schritt 4 — Build, Run und Live-Reload

    Meine gängigen Befehle:

  • docker compose build — baut Images
  • docker compose up — startet alle Services und zeigt Logs
  • docker compose up -d — startet im Hintergrund
  • Wenn etwas nicht wie erwartet läuft, nutze:

  • docker compose logs -f service
  • docker compose exec service sh oder bash — für Live-Debug im Container
  • Für Node:Installiere im Container nodemon (oder nutze dev-deps) und starte das Skript mit nodemon, damit Änderungen sofort geladen werden. Bei Python empfehle ich Flask/Django mit reload-Flag.

    Praktische Tipps und Fehlerquellen

    Einige Fallen, die ich häufig sehe:

  • Ports doppelt belegt: Prüfe mit lsof -i oder Windows Task Manager, wenn der Container nicht binden kann.
  • Volume-Overwrites: Ein Bind-Mount überschreibt automatisch Dateien im Image-Workdir — das kann Build-Artefakte verdecken. Nutze Bind-Mounts nur in dev.
  • Windows/WSL: Dateisystemperformance kann leiden. Nutze WSL2 für bessere Performance oder setze DDEV/Lando/Mutagen, wenn du viele kleine Dateien hast.
  • Umgebungsvariablen: Keine sensiblen Werte ins Repo. Nutze .env-Dateien lokal (in .gitignore) oder Docker Secrets bei sensiblen Daten.
  • Image-Größe und Multi-Stage Builds

    Je größer das Image, desto länger dauern Push/Pull im CI/CD und du brauchst mehr Speicher. Multi-Stage-Builds reduzieren die Größe, indem Build-Tools und Node-Module im finalen Image weggelassen werden. Bei statischen Frontends können einfache NGINX-Images sehr klein und performant sein.

    Sicherheit und Best Practices

    Auch lokal solltest du auf Sicherheit achten:

  • Arbeite nicht als root im Container (USER-Anweisung im Dockerfile)
  • Beschränke Ports auf localhost, wenn du Services nicht nach außen freigeben willst
  • Regelmäßig Images neu bauen, um Sicherheitsupdates der Basis-Images zu übernehmen
  • Verwalte Secrets nicht im Klartext im Repo
  • Backup, Persistenz und Datenbanken

    Setze für Datenbanken immer benannte Volumes oder Host-Ordner, damit Daten auch nach Neustarts erhalten bleiben. Beispiel in docker-compose:

    volumes:
    db-data:

    Und im Service: volumes: - db-data:/var/lib/postgresql/data

    So kannst du Container kurieren, ohne deine Testdaten zu verlieren.

    Mein Quick-Workflow für eine Stunde

    Wenn ich heute eine neue Webapp containerisiere, arbeite ich so:

  • 10 Min: Projekt prüfen, .dockerignore anlegen
  • 20 Min: Dockerfile schreiben (Dev & Prod Mode), erstes Build
  • 15 Min: docker-compose mit DB/Cache, Volumes und env-Datei
  • 15 Min: Starten, Logs prüfen, Hot-Reload fixen und erstes End-to-End testen
  • Oft reicht das — in 45–60 Minuten hast du ein wiederholbares Setup, das sich leicht in CI integrieren lässt.

    Wenn du magst, kann ich dir ein konkretes Dockerfile und eine docker-compose.yml für deine Technologie (Node, Python, Ruby, PHP) schreiben — gib mir kurz die Projektstruktur oder package.json, und ich erstelle eine maßgeschneiderte Vorlage, die du direkt in dein Repo kopieren kannst.