← Alle Artikel
Zuletzt aktualisiert: 2026-03-30

Git-Pipeline kaputt? So beheben Sie häufige CI/CD- und Git-Probleme

Kaputte CI/CD-Pipelines und Git-Probleme beheben. GitHub Actions Debugging, Merge-Konflikte, Force-Push-Recovery.

Kurzfassung (TL;DR)

CI/CD-Pipelines scheitern aus vorhersehbaren Gründen: Dependency-Fehler, Docker-Build-Probleme, instabile Tests, Merge-Konflikte, falsch konfigurierte Secrets oder fehlende Berechtigungen. Dieser Artikel führt systematisch durch die Fehlersuche für jedes Szenario — mit konkreten Befehlen für GitHub Actions und GitLab CI. Das wichtigste Prinzip: Zuerst die Logs lesen, dann lokal reproduzieren und die eigentliche Ursache beheben, statt blind einen Retry zu starten.

Voraussetzungen

CI/CD-Logs lesen

GitHub Actions

Die Logs von GitHub Actions sind über den Actions-Tab im Repository erreichbar. Jeder Workflow-Lauf zeigt die einzelnen Jobs an, und jeder Job lässt sich in seine Schritte aufklappen. Fehlgeschlagene Schritte sind rot markiert und automatisch aufgeklappt.

Logs über die Kommandozeile abrufen:

# Letzte Workflow-Läufe anzeigen
gh run list --limit 10

# Einen bestimmten fehlgeschlagenen Lauf anzeigen
gh run view 123456789

# Vollständige Logs herunterladen für Offline-Analyse
gh run view 123456789 --log

# Einen laufenden Workflow in Echtzeit verfolgen
gh run watch 123456789

Worauf man in der Ausgabe achten sollte:

GitLab CI

GitLab-CI-Logs befinden sich unter CI/CD > Pipelines in der Seitenleiste. Man klickt auf die Pipeline und dann auf den jeweiligen Job. GitLab bietet eine nützliche "Im Log suchen"-Funktion direkt im Browser.

# Mit der GitLab CLI (glab)
glab ci status

# Log eines bestimmten Jobs anzeigen
glab ci view <job-id>

# Einen fehlgeschlagenen Job erneut starten
glab ci retry <job-id>

Auf beiden Plattformen kann man Debug-Logging aktivieren, wenn die Standardlogs nicht ausreichen:

# GitHub Actions — im env-Abschnitt des Workflows ergänzen
env:
  ACTIONS_RUNNER_DEBUG: true
  ACTIONS_STEP_DEBUG: true
# GitLab CI — Variable in der Pipeline-UI oder .gitlab-ci.yml setzen
variables:
  CI_DEBUG_TRACE: "true"

Achtung: Debug-Logging kann Secrets in der Log-Ausgabe offenlegen. Nur vorübergehend und bei privaten Repositories aktivieren.

Häufige Pipeline-Fehler

Dependency-Installationsfehler

Der häufigste CI-Fehler ist eine fehlgeschlagene Dependency-Installation. Typischerweise zeigt sich das als Paketmanager-Fehler im Install-Schritt.

Symptome:

Diagnose und Behebung:

# Zuerst lokal reproduzieren — Caches leeren, um CI-Bedingungen nachzustellen
npm cache clean --force
rm -rf node_modules package-lock.json
npm install

# Für Python-Projekte
pip install --no-cache-dir -r requirements.txt

# Prüfen, ob eine bestimmte Paketversion noch existiert
npm view some-package@1.2.3

# Lock-Datei fixieren und committen
git add package-lock.json
git commit -m "fix: pin dependency lock file"

Häufige Ursachen:

# GitHub Actions — Node.js-Version festlegen
steps:
  - uses: actions/setup-node@v4
    with:
      node-version: '20.11.0'
      cache: 'npm'

Docker-Build-Fehler

Docker-Builds scheitern in CI aus anderen Gründen als lokal — hauptsächlich wegen Caching-Unterschieden und Ressourcenlimits.

# Den CI-Build lokal ohne Cache reproduzieren
docker build --no-cache -t myapp:test .

# Multi-Stage-Build-Probleme isolieren
docker build --target builder -t myapp:builder .

# Layers inspizieren bei Größenproblemen
docker history myapp:test

Häufige Docker-Build-Fehler:

# GitLab CI — Docker-Build mit Layer-Caching
build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  variables:
    DOCKER_BUILDKIT: "1"
  script:
    - docker build
        --cache-from $CI_REGISTRY_IMAGE:latest
        --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --tag $CI_REGISTRY_IMAGE:latest
        .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

Test-Fehler

Tests, die lokal bestehen, aber in CI fehlschlagen — umgangssprachlich "Flaky Tests" — gehören zu den frustrierendsten Problemen.

Häufige Ursachen:

# Tests lokal in derselben Reihenfolge wie CI ausführen
npx jest --runInBand --forceExit

# Für Python: Testreihenfolge randomisieren, um Abhängigkeiten aufzudecken
pip install pytest-randomly
pytest --randomly-seed=12345

# Nur den fehlschlagenden Test mit ausführlicher Ausgabe laufen lassen
npx jest --verbose --testPathPattern="failing-test"
pytest -xvs tests/test_failing.py
# GitHub Actions — Service-Container für Integrationstests
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - run: npm test
        env:
          DATABASE_URL: postgres://postgres:testpass@localhost:5432/testdb

Merge-Konflikte lösen

Merge-Konflikte sind im engeren Sinne kein CI-Fehler, blockieren aber Merges und können Pipeline-Fehler verursachen, wenn automatische Merges versucht werden.

# Branch mit dem aktuellen Zielbranch aktualisieren
git fetch origin
git checkout feature-branch
git rebase origin/main

# Bei Konflikten zeigt Git die betroffenen Dateien an
git status

# Jede Datei mit Konflikten öffnen und die Markierungen auflösen:
#   <<<<<<< HEAD
#   (eigene Änderungen)
#   =======
#   (eingehende Änderungen)
#   >>>>>>> origin/main

# Nach dem Auflösen jeder Datei
git add aufgeloeste-datei.txt

# Rebase fortsetzen
git rebase --continue

# Falls es völlig schiefläuft — abbrechen und von vorne anfangen
git rebase --abort

Für komplexe Konflikte empfiehlt sich ein Drei-Wege-Merge-Tool:

# Merge-Tool konfigurieren (z. B. VS Code)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'

# Merge-Tool während der Konfliktauflösung starten
git mergetool

Merge vs. Rebase: Rebase erzeugt eine sauberere Historie, schreibt aber Commits um. Auf gemeinsam genutzten Branches lieber git merge origin/main statt Rebase verwenden, um das Umschreiben von Commits zu vermeiden, die andere bereits gepullt haben.

Nach Force Push wiederherstellen

Ein Force Push (git push --force) überschreibt die Remote-Historie. Falls jemand versehentlich einen Force Push über bestehende Arbeit ausführt, ist eine Wiederherstellung über git reflog möglich.

# Das Reflog zeichnet jede Position auf, auf die HEAD gezeigt hat
git reflog

# Die Ausgabe sieht so aus:
# a1b2c3d HEAD@{0}: pull: Fast-forward
# e4f5g6h HEAD@{1}: commit: add user auth module
# i7j8k9l HEAD@{2}: commit: update API endpoint

# Den Commit vor dem Force Push finden und dorthin zurücksetzen
git reset --hard HEAD@{1}

# Oder einen neuen Branch vom verlorenen Commit erstellen
git checkout -b wiederhergestellte-arbeit e4f5g6h

# Den wiederhergestellten Branch pushen
git push origin wiederhergestellte-arbeit

Wichtig: Das Reflog existiert nur auf Rechnern, die die Commits lokal hatten. Falls man die Commits nie gepullt hat, lassen sie sich vom eigenen Rechner nicht wiederherstellen. Bei Teammitgliedern nachfragen oder CI-Build-Artefakte prüfen, die den verlorenen Commit ausgecheckt haben könnten.

Um Force-Push-Katastrophen zu verhindern, stattdessen --force-with-lease verwenden:

# Sicherer Force Push — schlägt fehl, wenn Remote Commits hat, die man nicht kennt
git push --force-with-lease origin feature-branch

# Als Standardverhalten festlegen
git config --global alias.fpush 'push --force-with-lease'

Docker-Builds in CI

Optimierte Docker-Builds in CI reduzieren die Build-Zeiten drastisch. Die wichtigsten Stellschrauben sind Layer-Caching, Multi-Stage-Builds und BuildKit.

# GitHub Actions — Docker-Build mit GitHub Container Registry und Cache
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/setup-buildx-action@v3

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Das Dockerfile so strukturieren, dass Cache-Hits maximiert werden:

# GUT: Zuerst Dependency-Dateien kopieren, installieren, dann Quellcode kopieren
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]

Secrets und Umgebungsvariablen in Pipelines

Falsch konfigurierte Secrets sind ein stiller Pipeline-Killer — die Pipeline läuft durch, aber die Anwendung scheitert zur Laufzeit oder wird mit fehlender Konfiguration deployt.

GitHub Actions Secrets

# Secrets in einem Workflow referenzieren
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
        run: |
          echo "Deployment mit konfigurierten Secrets..."
          ./deploy.sh
# Secrets über die CLI setzen
gh secret set API_KEY --body "sk-abc123"

# Aus einer Datei setzen (nützlich für mehrzeilige Secrets wie SSH-Schlüssel)
gh secret set DEPLOY_KEY < ~/.ssh/deploy_key

# Konfigurierte Secrets auflisten (Werte sind verborgen)
gh secret list

# Umgebungsspezifische Secrets setzen
gh secret set API_KEY --env production --body "sk-prod-xyz"

GitLab-CI-Variablen

# .gitlab-ci.yml — Variablen verwenden
deploy:
  stage: deploy
  script:
    - echo "Deployment auf $DEPLOY_HOST"
    - scp -i $SSH_PRIVATE_KEY ./build/* user@$DEPLOY_HOST:/app/
  variables:
    DEPLOY_HOST: "production.example.com"
  only:
    - main

Fehlende Secrets diagnostizieren:

# Im CI-Skript prüfen, ob ein Secret gesetzt ist (ohne es auszugeben)
if [ -z "$API_KEY" ]; then
  echo "FEHLER: API_KEY ist nicht gesetzt"
  exit 1
fi
echo "API_KEY ist konfiguriert (Länge: ${#API_KEY})"

Häufige Stolperfallen:

Branch Protection und Deploy Keys

Branch-Protection-Regeln

Branch Protection verhindert direkte Pushes und erzwingt Qualitäts-Gates, kann aber auch automatisierte Workflows blockieren, wenn die Konfiguration nicht stimmt.

# GitHub — Branch Protection über die CLI konfigurieren
gh api repos/{owner}/{repo}/branches/main/protection \
  --method PUT \
  --field required_status_checks='{"strict":true,"contexts":["ci/test","ci/build"]}' \
  --field enforce_admins=true \
  --field required_pull_request_reviews='{"required_approving_review_count":1}' \
  --field restrictions=null

Wenn die Pipeline wegen Branch Protection fehlschlägt:

Deploy Keys

Deploy Keys bieten repository-spezifischen SSH-Zugang, ohne ein persönliches Konto zu verwenden.

# Deploy-Key-Paar erzeugen
ssh-keygen -t ed25519 -C "deploy-key-myrepo" -f deploy_key -N ""

# Öffentlichen Schlüssel zum Repository hinzufügen (standardmäßig nur Lesezugriff)
gh repo deploy-key add deploy_key.pub --title "CI Deploy Key"

# Für Schreibzugriff (Pushes, Tags)
gh repo deploy-key add deploy_key.pub --title "CI Deploy Key" --allow-write

# Privaten Schlüssel als CI-Secret hinzufügen
gh secret set DEPLOY_KEY < deploy_key

# Lokales Schlüsselpaar aufräumen
rm deploy_key deploy_key.pub

Den Deploy Key in einem GitHub-Actions-Workflow verwenden:

steps:
  - uses: actions/checkout@v4
    with:
      ssh-key: ${{ secrets.DEPLOY_KEY }}

Fehlerbehebung — Schnellreferenz

SymptomWahrscheinliche UrsacheLösung
npm ci scheitert mit ERESOLVELock-Datei nicht synchronLokal npm install ausführen, package-lock.json committen
Docker-Build scheitert bei COPYDatei durch .dockerignore ausgeschlossen.dockerignore prüfen, Pfade anpassen
Tests lokal erfolgreich, in CI nichtFehlende Umgebungsvariablen oder ServicesService-Container ergänzen, Secret-Konfiguration prüfen
Pipeline startet nieFalscher Branch- oder Pfad-Filteron: / only: / rules: in der CI-Konfiguration prüfen
Permission denied beim PushBranch Protection oder fehlendes TokenDeploy Key oder PAT mit passenden Scopes verwenden
Secrets erscheinen als leere StringsFork-PR oder geschützte VariableAuf dem Basis-Repo-Branch ausführen, Variablenschutz prüfen
Force Push hat Commits verlorenKein --force-with-lease verwendetÜber git reflog wiederherstellen, Alias einrichten
Cache funktioniert bei Docker nichtBuildKit nicht aktiviertDOCKER_BUILDKIT=1 setzen, cache-from verwenden

Prävention und Best Practices

Experten-Hilfe gebraucht?

Pipeline immer noch kaputt? Ich repariere live per Screensharing. €49.

Jetzt buchen — €49

100% Geld-zurück-Garantie

HR

Harald Roessler

Infrastructure Engineer mit 20+ Jahren Erfahrung. Gründer der DSNCON GmbH.