docker compose up vs docker compose run - der Unterschied

docker compose up vs docker compose run - der Unterschied

up und run klingen ähnlich, verfolgen aber grundlegend verschiedene Ziele — der falsche Befehl in einem Migrations-Script kostet Zeit und Nerven.

docker compose up

Startet alle Services und hält sie am Laufen:

docker compose up                  # Vordergrund, Logs in Terminal
docker compose up -d               # Hintergrund (detached)
docker compose up app db           # Nur bestimmte Services
docker compose up --build          # Images vorher neu bauen
docker compose up --force-recreate # Container neu erstellen, auch wenn unverändert
  • Startet alle Services mit ihren Abhängigkeiten
  • Bleibt dauerhaft aktiv
  • Hält depends_on-Reihenfolge ein
  • Zeigt aggregierte Logs aller Services

docker compose run

Führt einen einmaligen Befehl in einem neuen Container aus:

docker compose run app python manage.py migrate
docker compose run app bash
docker compose run --rm app python seed.py
  • Startet einen neuen Container für den Service
  • Startet automatisch depends_on-Abhängigkeiten
  • Endet, wenn der Befehl fertig ist
  • --rm: Container nach Abschluss automatisch löschen

Vergleich auf einen Blick

| | up | run |
|---|---|---|
| Ziel | Alle Services dauerhaft starten | Einmaligen Befehl ausführen |
| Endet automatisch | Nein | Ja (wenn Befehl fertig) |
| Startet Abhängigkeiten | Ja | Ja |
| Mehrere Services | Ja | Nein (ein Service) |
| Typischer Einsatz | Produktions-/Dev-Start | Migrationen, Skripte, Debugging |

Nützliche run-Flags

# Container nach Ausführung löschen (fast immer sinnvoll)
docker compose run --rm app python migrate.py

# Abhängigkeiten nicht starten (wenn DB schon läuft)
docker compose run --no-deps app python check.py

# Umgebungsvariable überschreiben
docker compose run -e APP_ENV=test app python test.py

# Port mappen (run mappt keine ports: aus der Compose-Definition)
docker compose run -p 8080:8080 app

# Interaktive Shell
docker compose run --rm app bash

# Anderen Benutzer
docker compose run --user root app sh

run vs. exec

Drei Befehle, drei verschiedene Szenarien:

# Neuen Container für einmaligen Befehl starten
docker compose run --rm app python migrate.py

# Befehl in bereits laufendem Container ausführen
docker compose exec app python status.py

# Interaktive Shell in laufendem Container
docker compose exec -it app bash

| | run | exec |
|---|---|---|
| Container muss laufen | Nein | Ja |
| Startet neuen Container | Ja | Nein |
| Für Einmalaufgaben | Ja | Nein |
| Für Debugging laufender Container | Nein | Ja |

Typisches Migrations-Pattern

# DB starten und warten
docker compose up -d db
docker compose run --rm app python wait-for-db.py

# Migration ausführen
docker compose run --rm app python manage.py migrate

# App starten
docker compose up -d app

Oder in einem Schritt mit depends_on und healthcheck — dann übernimmt Compose das Warten:

docker compose run --rm app python manage.py migrate   # DB startet automatisch als Abhängigkeit
docker compose up -d

Ports und run

run mappt keine Ports aus dem ports:-Block der Compose-Definition. Das ist eine häufige Falle:

# Port explizit angeben wenn nötig
docker compose run -p 8080:8080 app    # Nicht der ports:-Block aus compose.yml

run ist das richtige Werkzeug für alles, was einmal läuft und dann fertig ist — Migrationen, Datei-Generierung, Seed-Skripte, Tests. up ist für Dienste, die dauerhaft verfügbar sein sollen.