Zum Inhalt springen
Docker Fortgeschritten 30 min

docker-compose fuer Webapps

Mehrere Services orchestrieren: docker-compose fuer Webapp + Datenbank + Cache. Der Standard fuer lokale Entwicklung und kleine Produktions-Setups.

Aktualisiert:
Inhaltsverzeichnis

docker-compose fuer Webapps

Ein einzelner Container ist selten alles, was du brauchst. Echte Apps haben eine Webapp + Datenbank + Cache + โ€ฆ - und diese zusammen zu starten ist mit docker compose ein Zweizeiler.

Was ist Compose?

docker compose ist ein Tool, das mehrere Container per YAML-Datei definiert und gemeinsam verwaltet. Eine Befehlszeile wie:

docker compose up

โ€ฆ startet deine ganze App-Landschaft: Webserver, DB, Cache, Queue.

Die erste compose.yaml

Ein einfacher Node.js-Server + PostgreSQL + Redis:

compose.yaml:

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/app_db
      REDIS_URL: redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app_db
    volumes:
      - db-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  db-data:

Starten:

docker compose up

Drei Container starten, vernetzen sich, die App ist unter http://localhost:3000 erreichbar.

Die wichtigsten Kommandos

docker compose up              # startet alles (zeigt Logs)
docker compose up -d           # detached (Hintergrund)
docker compose down            # stoppt und loescht alle Container
docker compose down -v         # + Volumes loeschen
docker compose ps              # welche Services laufen
docker compose logs            # Logs aller Services
docker compose logs -f web     # nur web, live
docker compose exec web bash   # Shell in web-Container
docker compose restart web     # nur web neustarten
docker compose pull            # aktuelle Images ziehen
docker compose build           # Eigene Images neu bauen

Die YAML-Sektionen

services:

Das Herzstueck - jeder Block ist ein Container.

services:
  name-des-service:
    image: nginx:alpine
    # ODER
    build: ./pfad-zum-dockerfile
    # ODER
    build:
      context: .
      dockerfile: Dockerfile.prod

ports:

Port-Mapping wie bei docker run -p:

ports:
  - "3000:3000"         # Host:Container
  - "127.0.0.1:5432:5432"  # nur an Localhost binden

environment:

environment:
  NODE_ENV: production
  DATABASE_URL: postgres://...

Oder als Liste:

environment:
  - NODE_ENV=production
  - DATABASE_URL=postgres://...

volumes:

Volumes auf Service-Ebene:

volumes:
  - ./app:/app                    # Bind mount
  - db-data:/var/lib/postgres      # Named volume

Und Volumes auf Top-Ebene deklarieren:

volumes:
  db-data:

depends_on:

Reihenfolge beim Start:

depends_on:
  - db
  - cache

Wichtig: depends_on wartet nur auf Container-Start, nicht auf โ€œbereitโ€ - Postgres startet schnell, aber bis es Anfragen annimmt, dauert es ein paar Sekunden. Besser ist ein Healthcheck:

services:
  db:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      retries: 5

  web:
    build: .
    depends_on:
      db:
        condition: service_healthy

Jetzt wartet web wirklich, bis DB erreichbar ist.

Networking in Compose

Automatisches Netzwerk

Compose erstellt automatisch ein Netzwerk - alle Services sehen sich ueber den Service-Namen:

environment:
  DATABASE_URL: postgres://app:secret@db:5432/app_db
                                     ^^^
                               Service-Name statt localhost/IP

Das ist einer der grossen Vorteile von Compose.

Eigene Netzwerke

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend

networks:
  frontend:
  backend:

So kann web die DB erreichen, aber andere Frontend-Dienste nicht.

Bind Mounts fuer Live-Reload

In der Entwicklung willst du Code-Aenderungen sofort sehen:

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - ./src:/app/src               # Live-Code-Sync
      - /app/node_modules            # node_modules im Container lassen
    environment:
      NODE_ENV: development
    command: npm run dev

Der Trick: ./src:/app/src synchronisiert deinen Code. /app/node_modules ohne Bind-Mount verhindert, dass dein lokales node_modules drueber geschoben wird.

Env-Files

Secrets und Env-Variablen gehoeren nicht in die compose.yaml eingecheckt:

.env:

POSTGRES_PASSWORD=mein-geheimes-passwort
API_KEY=sk-...

compose.yaml:

services:
  web:
    env_file:
      - .env

  db:
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Die ${VARIABLE}-Syntax liest aus .env zur Startzeit.

Ein produktiver Stack

Beispiel: Next.js App + PostgreSQL + Redis + Adminer

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      retries: 5
    restart: unless-stopped

  cache:
    image: redis:7-alpine
    volumes:
      - cache-data:/data
    restart: unless-stopped

  adminer:
    image: adminer:latest
    ports:
      - "8080:8080"
    depends_on:
      - db

volumes:
  db-data:
  cache-data:

Starten:

docker compose up -d

Du hast jetzt:

  • App unter http://localhost:3000
  • DB-GUI (Adminer) unter http://localhost:8080
  • PostgreSQL und Redis im Hintergrund

Mehrere Umgebungen: Dev und Prod

Du kannst Overrides nutzen. Basis-Datei + env-spezifische Overrides:

compose.yaml (Basis):

services:
  app:
    build: .
    ports: ["3000:3000"]

compose.override.yaml (Dev, automatisch geladen):

services:
  app:
    volumes:
      - ./src:/app/src
    environment:
      NODE_ENV: development
    command: npm run dev

compose.prod.yaml:

services:
  app:
    restart: always
    environment:
      NODE_ENV: production

Prod starten:

docker compose -f compose.yaml -f compose.prod.yaml up -d

Scale - mehrere Instanzen

docker compose up -d --scale web=3

Startet drei web-Container. Wichtig: Die Ports muessen dann dynamisch sein, oder ein Loadbalancer davor.

Resource-Limits

services:
  app:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

Wichtig fuer Produktion - verhindert, dass eine App den ganzen Server beansprucht.

Production-Hinweise

Compose ist fuer:

  • Lokale Entwicklung
  • Demo-Setups
  • Kleine Produktions-Deployments (Single-Host)

Fuer grosses Produktion-Scale (viele Server, Auto-Scaling) nutzt du Kubernetes oder Plattformen wie Google Cloud Run, AWS ECS, Fly.io.

Zusammenfassung

  • compose.yaml definiert mehrere Services deklarativ
  • docker compose up/down startet/stoppt alles
  • Services sehen sich ueber Namen im internen Netzwerk
  • Healthchecks + condition: service_healthy sichern Start-Reihenfolge
  • Volumes fuer Daten, env_file fuer Secrets
  • Override-Dateien fuer Dev vs. Prod

Damit hast du die Werkzeuge, um echte Multi-Service-Apps lokal zu entwickeln und klein zu deployen. Im weiteren Kurs vertiefen wir Docker-Registries, Deployment zu Cloud-Runtimes und den Einstieg in Kubernetes.

Zurรผck zum Docker Kurs