2. Oktober 2026
Das Fehlerbild
Ein konkreter Fall aus GitHub Actions: Zwei Merges lagen etwa zehn Sekunden auseinander. Beide Actions-Runs waren grün. Danach lief in Produktion trotzdem der ältere Commit.
Das wirkt zunächst widersprüchlich. Ein erfolgreicher Deploy sagt aber nur, dass der jeweilige Workflow technisch erfolgreich durchgelaufen ist. Er garantiert nicht, dass genau dieser Stand am Ende aktiv bleibt.
Die Kombination aus parallelen Pipelines und einem veränderbaren Docker latest Tag erzeugt eine klassische Deploy Race Condition.
Wie das Race entsteht
Angenommen, Commit A wird gemergt und startet Pipeline A. Kurz darauf startet Commit B Pipeline B.
Beide Pipelines bauen ein Container-Image und pushen es unter denselben Tag, etwa latest. Dieser Tag ist keine feste Referenz auf einen bestimmten Build. Er zeigt immer auf das Image, das zuletzt unter diesem Namen gepusht wurde.
Ein typischer Ablauf kann so aussehen:
| Zeitpunkt | Pipeline A | Pipeline B |
|---|---|---|
| Start | Build für Commit A startet | |
| Kurz danach | Build für Commit B startet | |
| Push | Image A wird als latest gepusht | |
| Push | Image B wird als latest gepusht | |
| Deploy | Pipeline B deployt latest | |
| Deploy | Pipeline A deployt ebenfalls latest |
Je nach Timing kann dabei mehr als ein Problem entstehen:
- Der Tag
latestzeigt beim Deploy auf ein anderes Image als beim vorherigen Schritt. - Ein älterer Workflow beendet seinen Deploy nach einem neueren Workflow.
- Ein Deploy lädt einen Tag, dessen Inhalt inzwischen überschrieben wurde.
Welcher Build zuletzt pusht und welcher Workflow zuletzt deployt, entscheidet dann nicht die Commit-Reihenfolge, sondern das Timing. Beide Läufe können grün sein, obwohl das Ergebnis falsch ist.
Mein wichtigster Punkt: Ein grüner Pipeline-Status ist kein Beweis für eine korrekte Reihenfolge in Produktion.
Fix 1: Deploys pro Umgebung serialisieren
Für GitHub Actions concurrency sollte es pro Zielumgebung genau eine Deploy-Ausführung geben. Wenn ein neuer Commit kommt, soll der ältere, noch laufende Deploy nicht weiterarbeiten.
Ein mögliches Muster im Workflow:
concurrency:
group: production-deploy
cancel-in-progress: trueDamit erhält die Umgebung eine gemeinsame Sperre. Startet ein neuer Workflow für dieselbe Gruppe, wird ein laufender älterer Lauf abgebrochen.
Die Gruppen sollten zur Umgebung passen. Beispielsweise braucht Staging eine andere Gruppe als Produktion:
concurrency:
group: deploy-production
cancel-in-progress: trueDas ist keine allgemeine Build-Sperre. Builds dürfen häufig parallel laufen. Entscheidend ist, dass der Abschnitt mit Wirkung auf dieselbe Umgebung nicht konkurriert.
Fix 2: Unveränderliche Tags deployen
Serialisierung verhindert viele Timing-Probleme. Sie ersetzt aber keine eindeutige Artefakt-Referenz.
Statt latest sollte der Deploy einen unveränderlichen Tag verwenden, beispielsweise den Commit-SHA. Dann referenziert jeder Deploy exakt das Image, das zu diesem Commit gebaut wurde.
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Push image
run: docker push app:${{ github.sha }}
- name: Deploy
run: deploy --image app:${{ github.sha }}Der konkrete Deploy-Befehl hängt von der Zielplattform ab. Das Prinzip bleibt gleich: Build und Deploy verwenden dieselbe unveränderliche Kennung.
Ein zusätzlicher beweglicher Tag kann für lokale Entwicklung oder manuelle Tests sinnvoll sein. Er sollte aber nicht die alleinige Referenz für produktive Deploys sein.
Übertragung auf ECR und ECS
Dieses Muster ist nicht auf ein bestimmtes Zielsystem beschränkt. Bei Container-Deploys auf AWS gilt es ebenso: Ein Image in ECR kann unter einem Tag referenziert werden, und ein AWS ECS Deployment kann diesen Tag als Image-Referenz verwenden.
Wenn zwei Pipelines denselben veränderbaren Tag verwenden und parallel arbeiten, bleibt das Grundproblem bestehen. ECR und ECS ändern nichts daran, dass ein Tag veränderbar ist und dass die zeitliche Reihenfolge konkurrierender Deploys relevant bleibt.
Für ein belastbares AWS ECS Deployment gilt daher dieselbe Regel:
- Image mit einer unveränderlichen Kennung bauen, etwa dem Commit-SHA.
- Genau diese Kennung nach ECR pushen.
- Genau diese Image-Referenz im ECS-Deploy verwenden.
- Deploys pro Umgebung mit GitHub Actions concurrency serialisieren.
So wird aus einem schwer nachvollziehbaren Timing-Problem ein überprüfbarer Zustand: Aus dem laufenden Image-Tag lässt sich direkt ableiten, welcher Commit ausgerollt wurde.
Welchen Tag deployt eure Pipeline – und können zwei Deploys gleichzeitig laufen?