Temporal mit Docker installieren: Durable-Execution-Plattform für fehlertolerante Workflow-Orchestrierung
Temporal speichert jeden Workflow-Schritt in PostgreSQL, sodass kein Job nach Absturz oder Neustart verloren geht. Die Anleitung zeigt das Docker-Compose-Setup mit temporalio/server 1.32.1 und PostgreSQL 16, inklusive Update von 1.32.0.
Geprüft am 22.10.2026 · für temporal 1.32.1, temporal-ui 2.57.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Temporal ist eine Open-Source-Plattform für Durable Execution: Der Workflow-Zustand wird nach jedem Schritt in einer Datenbank gespeichert; bricht ein Worker ab, läuft der Workflow dort weiter, ohne eigene Fehlerbehandlung. Typische Einsätze: verteilte Transaktionen (Saga-Pattern), langlaufende Prozesse, Zahlungsabwicklung, Onboarding und KI-Agenten. Die Anleitung gilt für jeden Linux-Host mit Docker.
Voraussetzungen
- Docker Engine >= 24.x und das Docker Compose Plugin >= 2.x müssen installiert sein. Prüfen Sie das mit
docker compose version. Eine Installationsanleitung finden Sie unter Docker und Docker Compose auf Linux installieren. - git zum Klonen des
samples-server-Repositories (Skripte und dynamische Konfiguration sind Pflicht). - Mindestens 2 CPU-Kerne und 4 GB freier RAM (Temporal-Server + PostgreSQL + UI; mit Elasticsearch: 8 GB empfohlen), x86-64 oder ARM64.
- Mindestens 10 GB freier Festplattenplatz für Images und das PostgreSQL-Daten-Volume.
- Internet-Zugang für den initialen Image-Download.
- Sicherheitshinweis: Temporal-Dienste dürfen niemals direkt im Internet erreichbar sein. Behandeln Sie sie wie eine Datenbank und betreiben Sie sie ausschließlich in vertrauenswürdigen internen Netzwerken oder hinter einem Reverse Proxy mit Authentifizierung. Für HTTPS-Termination eignet sich z. B. Traefik als Docker-Reverse-Proxy.
Eckdaten auf einen Blick
| Eigenschaft | Wert |
|---|---|
| Temporal-Server-Image | temporalio/server:1.32.1 |
| Admin-Tools-Image | temporalio/admin-tools:1.32.1 |
| Web-UI-Image | temporalio/ui:2.57.0 |
| Datenbank-Image | postgres:16 |
| gRPC-Port (Pflicht) | 7233 |
| Web-UI-Port | 8080 |
| PostgreSQL-Port | 5432 (nur 127.0.0.1) |
| RAM-Bedarf | mind. 4 GB |
| Installationszeit | ca. 20 Minuten |
| Lizenz | MIT (Open Source) |
| Volume / Mount | Zweck |
|---|---|
| postgres_data | Alle Workflow-Historien und -Zustände in PostgreSQL (Named Volume, Pflicht für Persistenz) |
| ./dynamicconfig | Enthält development-sql.yaml für Rate-Limits und Feature-Flags (Pflicht, muss vor Start existieren) |
| ./scripts | Shell-Skripte für Schema-Setup und Namespace-Anlegen (aus samples-server-Repository) |
Schritt 1: Repository klonen und Projektordner vorbereiten
Das offizielle temporalio/docker-compose-Repository ist archiviert. Die aktuellen Compose-Dateien und die zwingend benötigten Shell-Skripte liegen im samples-server-Repository. Klonen Sie es und wechseln Sie in das Unterverzeichnis:
git clone https://github.com/temporalio/samples-server.git
cd samples-server/composeIm Verzeichnis compose/ liegen fertige Varianten. Für PostgreSQL ohne Elasticsearch legen Sie im nächsten Schritt eine eigene compose.yaml auf Basis der docker-compose-postgres.yml an; Compose bevorzugt compose.yaml vor docker-compose.yml. scripts/ und dynamicconfig/ müssen relativ zum Compose-Verzeichnis liegen:
ls scripts/ dynamicconfig/Erwartet werden setup-postgres.sh, create-namespace.sh und dynamicconfig/development-sql.yaml.
Verifizieren: Der Befehl ls scripts/setup-postgres.sh scripts/create-namespace.sh dynamicconfig/development-sql.yaml muss alle drei Dateien ohne Fehler ausgeben. Fehlt eine Datei, prüfen Sie, ob Sie im Verzeichnis samples-server/compose stehen.
Schritt 2: .env-Datei mit gepinnten Versionen anlegen
Ersetzen Sie die mitgelieferte .env durch folgenden Inhalt. Pinnen Sie konkrete Tags statt :latest, weil Schemaänderungen zwischen Versionen inkompatibel sein können. Wichtig: TEMPORAL_VERSION und TEMPORAL_ADMINTOOLS_VERSION müssen identisch sein.
# Temporal Docker Compose: Versionspinning
POSTGRESQL_VERSION=16
TEMPORAL_VERSION=1.32.1
TEMPORAL_ADMINTOOLS_VERSION=1.32.1
TEMPORAL_UI_VERSION=2.57.0
# Datenbank-Zugangsdaten: eigenes Passwort setzen (z. B. openssl rand -hex 24)
POSTGRES_USER=temporal
POSTGRES_PASSWORD=IHR_SICHERES_PASSWORTDie Herstellerwerte (temporal/temporal) sind nur für Tests. Das Passwort muss vor dem ersten Start feststehen, PostgreSQL übernimmt es nur beim Anlegen des Volumes.
Verifizieren: cat .env zeigt alle sechs Variablen. Achten Sie darauf, dass keine Leerzeichen um das =-Zeichen stehen.
Schritt 3: compose.yaml anlegen
Legen Sie folgende compose.yaml an. Reihenfolge der fünf Dienste: PostgreSQL (Healthcheck), Schema-Setup durch temporal-admin-tools, dann temporal; sobald er gesund ist, folgen Namespace und UI.
services:
postgresql:
image: postgres:${POSTGRESQL_VERSION:-16}
container_name: temporal-postgresql
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432"
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-temporal}
POSTGRES_USER: ${POSTGRES_USER:-temporal}
networks:
- temporal-network
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U temporal"]
interval: 5s
timeout: 5s
retries: 60
start_period: 30s
temporal-admin-tools:
image: temporalio/admin-tools:${TEMPORAL_ADMINTOOLS_VERSION:-1.32.1}
container_name: temporal-admin-tools
restart: on-failure:6
depends_on:
postgresql:
condition: service_healthy
environment:
- DB=postgres12
- DB_PORT=5432
- POSTGRES_USER=${POSTGRES_USER:-temporal}
- POSTGRES_PWD=${POSTGRES_PASSWORD:-temporal}
- POSTGRES_SEEDS=postgresql
- SQL_PASSWORD=${POSTGRES_PASSWORD:-temporal}
networks:
- temporal-network
volumes:
- ./scripts:/scripts
entrypoint: ["/bin/sh"]
command: /scripts/setup-postgres.sh
temporal:
image: temporalio/server:${TEMPORAL_VERSION:-1.32.1}
container_name: temporal
restart: unless-stopped
depends_on:
temporal-admin-tools:
condition: service_completed_successfully
environment:
- DB=postgres12
- DB_PORT=5432
- POSTGRES_USER=${POSTGRES_USER:-temporal}
- POSTGRES_PWD=${POSTGRES_PASSWORD:-temporal}
- POSTGRES_SEEDS=postgresql
- BIND_ON_IP=0.0.0.0
- DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml
networks:
- temporal-network
ports:
- "7233:7233"
volumes:
- ./dynamicconfig:/etc/temporal/config/dynamicconfig
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "7233"]
interval: 5s
timeout: 3s
start_period: 30s
retries: 60
temporal-create-namespace:
image: temporalio/admin-tools:${TEMPORAL_ADMINTOOLS_VERSION:-1.32.1}
container_name: temporal-create-namespace
restart: on-failure:5
depends_on:
temporal:
condition: service_healthy
environment:
- TEMPORAL_ADDRESS=temporal:7233
- DEFAULT_NAMESPACE=default
networks:
- temporal-network
volumes:
- ./scripts:/scripts
entrypoint: ["/bin/sh"]
command: /scripts/create-namespace.sh
temporal-ui:
image: temporalio/ui:${TEMPORAL_UI_VERSION:-2.57.0}
container_name: temporal-ui
restart: unless-stopped
depends_on:
temporal:
condition: service_healthy
environment:
- TEMPORAL_ADDRESS=temporal:7233
- TEMPORAL_CORS_ORIGINS=http://localhost:3000
networks:
- temporal-network
ports:
- "8080:8080"
networks:
temporal-network:
driver: bridge
name: temporal-network
volumes:
postgres_data:Der PostgreSQL-Port ist nur an 127.0.0.1 gebunden; Temporal greift über das interne Docker-Netz zu.
Verifizieren: docker compose config --quiet darf keinen Fehler ausgeben und endet mit Exit-Code 0 (echo $?). Weil im Ordner auch die mitgelieferte docker-compose.yml liegt, gibt Compose hier und bei jedem weiteren Befehl zwei Warnzeilen aus: Found multiple config files with supported names: …/compose.yaml, …/docker-compose.yml und Using …/compose.yaml. Das ist erwartet, Compose verwendet die compose.yaml.
Schritt 4: Stack starten
Starten Sie alle Dienste im Hintergrund:
docker compose up -dDer Start dauert 2 bis 5 Minuten; die Reihenfolge steuern die depends_on-Bedingungen.
Logs aller Dienste live verfolgen:
docker compose logs -fVerifizieren: Nach ca. 2 bis 3 Minuten zeigt docker compose ps -a --format 'table {{.Name}}\t{{.Status}}' nach den beiden Warnzeilen aus Schritt 3 folgendes Bild. Ohne -a fehlen die beendeten admin-tools-Container in der Liste.
NAME STATUS
temporal Up 9 seconds (healthy)
temporal-admin-tools Exited (0) 10 seconds ago
temporal-create-namespace Exited (0) 3 seconds ago
temporal-postgresql Up 33 seconds (healthy)
temporal-ui Up 3 secondsBeide admin-tools-Container müssen mit Exit-Code 0 beendet sein, Server und PostgreSQL healthy. Ein anderer Exit-Code deutet auf Datenbankverbindung oder fehlende Skripte; prüfen Sie docker compose logs temporal-admin-tools.
Schritt 5: Web-UI aufrufen und Namespace prüfen
Öffnen Sie http://localhost:8080. Unter Namespaces muss „default“ als aktiv gelistet sein; ohne ihn lassen sich keine Workflows starten.
Prüfung per curl:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080Erwartet: 200. Nutzen Sie GET: Im Test mit UI 2.57.0 lieferte eine HEAD-Anfrage (curl -I) 405 Method Not Allowed, obwohl die UI im Browser lädt.
Verifizieren: Browser zeigt das Temporal-Dashboard ohne Fehlermeldung „Unable to reach server“. Namespace „default“ ist unter Settings > Namespaces sichtbar. Der gRPC-Port ist erreichbar:
docker exec temporal nc -z localhost 7233 && echo "gRPC OK"Erwartete Ausgabe: gRPC OK.
Schritt 6: Ersten Worker/Client verbinden
Worker und Clients verbinden sich über Port 7233 des Docker-Hosts, Namespace default:
# Go: client.Dial(client.Options{HostPort: "localhost:7233"})
# Python: await Client.connect("localhost:7233")
# TypeScript: new Client({ connection: { address: "localhost:7233" } })
# Namespace ist immer "default" (ohne explizite Angabe bereits Standard)Läuft der Worker als Container im Netz temporal-network, lautet die Adresse temporal:7233; nehmen Sie ihn dazu in der compose.yaml in dieses Netz auf.
Weitere Namespaces legen Sie mit einem kurzlebigen Admin-Tools-Container an (temporal-admin-tools ist nach dem Setup beendet, docker exec geht nicht):
docker compose run --rm --entrypoint temporal temporal-admin-tools \
operator namespace create --namespace mein-namespace --address temporal:7233Verifizieren: Starten Sie einen einfachen Worker und prüfen Sie, ob er sich ohne Verbindungsfehler registriert. In docker compose logs temporal sollten keine ERROR-Einträge bezüglich fehlender Verbindungen erscheinen.
Schritt 7: Updates durchführen
Server- und Admin-Tools-Version müssen gleich bleiben. Ändern Sie zuerst die Versionen in der .env, dann:
# .env anpassen: TEMPORAL_VERSION, TEMPORAL_ADMINTOOLS_VERSION, TEMPORAL_UI_VERSION
docker compose pull
docker compose up -dDas Schema-Update übernimmt der neue temporal-admin-tools-Container per update-schema. Vorher Release Notes lesen und die Datenbank per pg_dump sichern.
Von 1.31 auf 1.32: In der .env TEMPORAL_VERSION und TEMPORAL_ADMINTOOLS_VERSION auf 1.32.0, TEMPORAL_UI_VERSION auf 2.55.0, dann docker compose pull und docker compose up -d. Laut Release Notes ist der vereinheitlichte Query Converter für Visibility-Abfragen jetzt aktiv: Er lehnt Vergleiche mit falschem Datentyp (etwa CustomKeyword = 123) und Text-Attribute gegen leeren String ab; prüfen Sie eigene List-Filter. Übergangsweise holt system.visibilityEnableUnifiedQueryConverter: false in dynamicconfig/development-sql.yaml den alten zurück, mit 1.33 entfällt er. Activity Eager Execution ist standardmäßig an, Standalone Activities sind allgemein verfügbar. Die alten Worker-Versioning-APIs (Build-ID-Kompatibilität) sind letztmals enthalten; vor 1.33 auf die Worker Deployment APIs migrieren. Search Attributes verwalten Sie mit der temporal-CLI statt tctl. Im Test (Oktober 2026) lief 1.31.3 auf 1.32.0 ohne Eingriff: keine ausstehenden Schema-Updates, nach rund 20 Sekunden wieder bereit.
Von 1.32.0 auf 1.32.1: In der .env TEMPORAL_VERSION und TEMPORAL_ADMINTOOLS_VERSION auf 1.32.1, TEMPORAL_UI_VERSION auf 2.57.0, dann docker compose pull und docker compose up -d. Server 1.32.1 behebt laut Release Notes Fehler und ändert das Datenbankschema nicht. Die Temporal-CLI in admin-tools wechselt dabei von 1.9.0 auf 1.9.1. Deren Release Notes nennen gegenüber 1.8.3 drei Breaking Changes: temporal activity unpause kennt --reset-attempts und --reset-heartbeats nicht mehr, temporal activity describe -o json liefert die Felder eine Ebene tiefer unter info, und operator namespace update --active-cluster lässt sich nicht mehr mit anderen Optionen kombinieren. Prüfen Sie eigene Skripte, die diese Befehle nutzen. UI 2.56.0 wertet den Header X-HTTP-Method-Override nicht mehr aus (CVE-2026-37236) und kann eingehende Verbindungen optional per mTLS absichern. UI 2.57.0 bringt ein optionales Umgebungsbanner (TEMPORAL_UI_BANNER_TEXT, TEMPORAL_UI_BANNER_COLOR). Neue Pflichtvariablen gibt es nicht. Im Test (Oktober 2026) lief 1.32.0 auf 1.32.1 ohne Eingriff: temporal-admin-tools endete mit Exit-Code 0 und meldete found zero updates from current version 1.19 (Visibility-Datenbank: 1.14), der Server war 12 Sekunden nach docker compose up -d wieder bereit, Namespaces und Workflows samt Signalen blieben erhalten.
Verifizieren: docker compose images zeigt die neuen Image-Tags, docker compose ps -a meldet temporal wieder als healthy und temporal-admin-tools mit Exited (0). docker exec temporal temporal-server --version nennt die laufende Serverversion, im Test temporal version 1.32.1.
Troubleshooting / Typische Fehler
- „dependency failed to start: container temporal is unhealthy“:
BIND_ON_IP=0.0.0.0fehlt imtemporal-Dienst. Der Server lauscht dann nur auf der Container-IP statt auf allen Adressen, der Healthchecknc -z localhost 7233schlägt fehl undtemporalbleibt bei(health: starting). Im Test brachdocker compose up -dnach rund fünfeinhalb Minuten mit dieser Meldung ab,temporalstand danach auf(unhealthy). Zeile ergänzen,docker compose up -d. - „unable to create dynamic config client: unable to validate dynamic config“: Die in
DYNAMIC_CONFIG_FILE_PATHgenannte Datei fehlt, die Meldung endet mitno such file or directoryund der Server beendet sich mit Exit-Code 1../dynamicconfig/development-sql.yamlstammt aussamples-server, siehe Schritt 1. - admin-tools endet mit Exit-Code 1:
./scripts/setup-postgres.shfehlt oder ist nicht ausführbar, siehe Schritt 1. - „namespace not found“:
temporal-create-namespaceist fehlgeschlagen.docker compose logs temporal-create-namespaceprüfen, ggf. manuell anlegen:docker compose run --rm --entrypoint temporal temporal-admin-tools operator namespace create --namespace default --address temporal:7233. - Port-Konflikt auf 5432: Host-Port in der
compose.yamlauf127.0.0.1:5433:5432ändern. - Schema-Inkompatibilität nach Update:
TEMPORAL_VERSIONundTEMPORAL_ADMINTOOLS_VERSIONunterscheiden sich; synchron halten. - Veraltetes
auto-setup-Image:temporalio/auto-setupist deprecated; nutzen Sietemporalio/serverundtemporalio/admin-tools. - Workflow-History-Limit überschritten: Standardmäßig maximal 51.200 Events (50 × 1024) oder 50 MB je Workflow; lange KI-Agenten-Loops erreichen das. Lösung: Continue-as-New.
- Datenverlust nach docker compose down: Nur ohne Named Volume oder mit
-v;postgres_datableibt sonst erhalten.
Häufige Fragen
Kann ich Temporal für Produktions-Workloads mit Docker Compose betreiben?
Für kleine bis mittlere Installationen auf einem Server: ja. Für Hochverfügbarkeit und Skalierung bietet Temporal Helm-Charts für Kubernetes.
Wozu brauche ich Elasticsearch?
Nur für erweiterte Suche über eigene Workflow-Attribute (Search Attributes). Meist reicht PostgreSQL; Elasticsearch erhöht den RAM-Bedarf auf mindestens 8 GB.
Was ist der Unterschied zwischen Workflows und Activities?
Workflows sind deterministischer Orchestrierungs-Code: kein Netzwerkzugriff, keine Zufallszahlen, keine Systemzeit (datetime.now()). Activities sind die Arbeitseinheiten (API-Aufrufe, Datenbankabfragen) und werden automatisch wiederholt. Seiteneffekte gehören in Activities.
Wie prüfe ich, ob Temporal korrekt läuft?
Siehe Schritt 4 und 5: docker compose ps -a, curl auf Port 8080, nc -z auf Port 7233.
Wie erstelle ich weitere Namespaces?
Mit dem Befehl aus Schritt 6, etwa --namespace mein-namespace.
Unterstützen die offiziellen Images Apple Silicon (M1/M2)?
Ja, die Images gibt es für linux/amd64 und linux/arm64; auf Apple Silicon läuft der Stack nativ.
Fazit
Temporal liefert fehlertolerante Workflows ohne eigene Retry-Logik. Entscheidend sind BIND_ON_IP=0.0.0.0, das dynamicconfig-Verzeichnis vor dem ersten Start und gleiche Versionen von Server und Admin-Tools. Bei hoher Last folgt Kubernetes mit Helm-Charts.
Zur Absicherung des Stacks: Docker Compose absichern: Secrets, Healthchecks und Non-Root zeigt, wie Sie Passwörter aus der Compose-Datei herausnehmen und Container mit minimalen Rechten betreiben. Zur Datensicherung: MySQL & PostgreSQL Backup automatisieren erklärt, wie Sie die Temporal-Datenbanken regelmäßig per pg_dump sichern und extern ablegen.
Weiterführende Anleitungen und Quellen
- Docker Compose: Multi-Container-Stacks aufbauen, Grundlagen zu Compose-Syntax, depends_on und Healthchecks
- Docker-Netzwerke und Volumes richtig nutzen, Named Volumes und Bridge-Netzwerke verstehen
- PostgreSQL pg_dump und pg_restore, Backup und Migration der Temporal-Datenbank
- Windmill mit Docker installieren, alternative Developer-Workflow-Plattform im Vergleich
- n8n mit Docker: KI-Workflows und Automatisierung self-hosted, Low-Code-Automatisierung als Ergänzung zu Temporal
Offizielle Quellen: Temporalio Samples-Server-Repository (github.com/temporalio/samples-server/tree/main/compose), Temporal Self-Hosted Deployment Guide (docs.temporal.io/self-hosted-guide/deployment), Docker Hub: temporalio/server und temporalio/ui. Release Notes: Temporal Server v1.32.1, Temporal UI v2.56.0, Temporal UI v2.57.0, Temporal CLI v1.9.1.


