Bash · Retry · Backoff · Timeout · Resilienz · DevOps
Retry, Backoff und Timeout-Strategien
in Shell-Skripten

Shell-Skripte, die mit Netzwerkdiensten, APIs und externen Systemen interagieren, scheitern ohne Retry-Logik beim ersten transienten Fehler. Exponentielles Backoff mit Jitter, der timeout-Befehl und klare Max-Retry-Grenzen machen Shell-Skripte resilient — ohne dass ein Operator nachts manuell neu starten muss.

14 Min. Lesezeit Retry · Exponentielles Backoff · Jitter · timeout · Max-Retries Bash 4.x · 5.x · GNU Coreutils · Linux

1. Warum Retry-Logik in Shell-Skripten notwendig ist

Retry-Logik in Shell-Skripten ist keine optionale Verbesserung, sondern eine Grundvoraussetzung für alle Skripte, die mit externen Ressourcen interagieren. Netzwerkverbindungen sind fluktuierend, APIs haben Rate-Limits und transiente Fehler, Dienste starten nach einem Neustart langsam hoch, Datenbanken sind kurzzeitig nicht erreichbar. Ein Skript ohne Retry-Mechanismus schlägt beim ersten derartigen transienten Fehler ab und erfordert manuellen Eingriff — genau das, was durch Automatisierung vermieden werden soll.

Die drei Hauptprobleme ohne Retry in Shell-Skripten: Erstens, ein Skript bricht bei vorübergehenden Netzwerkfehlern sofort ab und muss manuell neu gestartet werden. Zweitens, ein Skript wartet unbegrenzt auf einen hängenden Befehl (fehlende Timeout-Logik). Drittens, mehrere Instanzen desselben Skripts überlasten nach einem Ausfall gleichzeitig den Zieldienst, weil alle zur selben Zeit in festen Intervallen wieder versuchen — das klassische Thundering-Herd-Problem, das ohne Jitter beim Backoff entsteht.

Die Lösung für alle drei Probleme: Eine wiederverwendbare Retry-Funktion mit exponentiellem Backoff, optionalem Jitter und dem GNU-Coreutils-Befehl timeout für Zeitlimits. Diese Komponenten lassen sich zu einer kompakten Bibliothek zusammenfassen, die in beliebigen Shell-Skripten eingebunden werden kann. Der Aufwand für die Implementierung ist gering, der Nutzen in Produktionsumgebungen erheblich.

2. Einfache Retry-Schleife: Grundmuster

Das einfachste Retry-Muster in Shell-Skripten ist eine for- oder while-Schleife, die einen Befehl bis zu N-mal ausführt und nach jedem fehlgeschlagenen Versuch eine feste Wartezeit einlegt. Die Struktur: Befehl ausführen, Exit-Code prüfen, bei 0 die Schleife beenden, bei Fehler warten und den Versuchszähler erhöhen. Nach N Versuchen ohne Erfolg gibt die Funktion den letzten Fehler-Exit-Code zurück. Das ist das Fundament der Retry-Logik, auf dem alle komplexeren Strategien aufbauen.

Der kritische Punkt beim einfachen Retry in Shell-Skripten: Das Skript muss mit set -euo pipefail laufen, aber die Retry-Funktion darf durch den Fehler des Befehls nicht abgebrochen werden. Die Lösung: Den Befehl mit || true oder in einem Kontext aufrufen, in dem set -e nicht greift — zum Beispiel nach || oder in einem if-Ausdruck. Das erhöht die Komplexität leicht, ist aber für korrekte Retry-Logik in Shell-Skripten unvermeidlich.


#!/usr/bin/env bash
# lib/retry.sh — Reusable retry library with exponential backoff and jitter
set -euo pipefail

# retry <max_attempts> <command> [args...]
# Simple retry with fixed wait interval
retry() {
  local max_attempts="$1"; shift
  local attempt=1
  local wait_seconds=1

  while (( attempt <= max_attempts )); do
    printf '[RETRY] Versuch %d/%d: %s\n' "$attempt" "$max_attempts" "$*" >&2
    if "$@"; then
      [[ $attempt -gt 1 ]] && printf '[RETRY] Erfolgreich nach %d Versuchen\n' "$attempt" >&2
      return 0
    fi
    local exit_code=$?
    printf '[RETRY] Fehlgeschlagen (Exit %d)\n' "$exit_code" >&2

    if (( attempt < max_attempts )); then
      printf '[RETRY] Warte %ds vor nächstem Versuch…\n' "$wait_seconds" >&2
      sleep "$wait_seconds"
    fi
    (( attempt++ ))
  done

  printf '[RETRY] Alle %d Versuche fehlgeschlagen\n' "$max_attempts" >&2
  return 1
}

# Usage examples
retry 3 curl -sf "https://api.example.com/health"
retry 5 rsync -avz --partial /data/ user@host:/backup/
retry 3 bin/magento cache:flush

3. Exponentielles Backoff: Wartezeit verdoppeln

Exponentielles Backoff in Shell-Skripten ist der Standard-Algorithmus für intelligentes Retry-Verhalten: Nach jedem fehlgeschlagenen Versuch wird die Wartezeit verdoppelt (oder mit einem anderen Faktor multipliziert). Statt nach 1, 1, 1, 1 Sekunden zu warten, wartet das Skript nach 1, 2, 4, 8 Sekunden — mit einer maximalen Wartezeit (Cap), um zu verhindern, dass die Pausen ins Stundenlange wachsen. Die typischen Parameter: Basis-Wartezeit 1 Sekunde, Multiplikator 2, maximale Wartezeit 60 Sekunden, maximale Versuche 5.

Die Implementierung des exponentiellen Backoff in Shell-Skripten nutzt die Bash-Arithmetik mit $(( base * 2 ** (attempt - 1) )). Da Bash nur Integer-Arithmetik beherrscht, sind Basis und Multiplikator ganze Zahlen. Für feinere Kontrolle kann awk oder python3 -c für Float-Arithmetik verwendet werden. In der Praxis ist Integer-Backoff für Shell-Skripte ausreichend — die Grenzen liegen in der Regel bei 1, 2, 4, 8, 16, 32 Sekunden, was die meisten transienten Probleme überbrückt, ohne übermäßig lang zu warten.


#!/usr/bin/env bash
set -euo pipefail

# retry_with_backoff <max_attempts> <base_wait> <max_wait> <command> [args...]
retry_with_backoff() {
  local max_attempts="$1"
  local base_wait="$2"      # seconds for first wait
  local max_wait="$3"       # cap for wait time
  shift 3
  local attempt=1
  local wait_seconds="$base_wait"

  while (( attempt <= max_attempts )); do
    printf '[BACKOFF] Versuch %d/%d: %s\n' "$attempt" "$max_attempts" "$*" >&2

    if "$@"; then
      [[ $attempt -gt 1 ]] && \
        printf '[BACKOFF] Erfolgreich nach %d Versuchen\n' "$attempt" >&2
      return 0
    fi
    local exit_code=$?

    if (( attempt < max_attempts )); then
      printf '[BACKOFF] Fehlgeschlagen (Exit %d) — warte %ds\n' \
        "$exit_code" "$wait_seconds" >&2
      sleep "$wait_seconds"

      # Double the wait, cap at max_wait
      wait_seconds=$(( wait_seconds * 2 ))
      (( wait_seconds > max_wait )) && wait_seconds="$max_wait"
    fi
    (( attempt++ ))
  done

  printf '[BACKOFF] Alle %d Versuche fehlgeschlagen: %s\n' "$max_attempts" "$*" >&2
  return 1
}

# Wait sequence: 1s, 2s, 4s, 8s (capped at 30s)
retry_with_backoff 5 1 30 \
  curl -sf --max-time 10 "https://api.mironsoft.de/v1/health"

# Longer waits for database availability (2s, 4s, 8s, 16s, 30s)
retry_with_backoff 5 2 30 \
  mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -e "SELECT 1" "$DB_NAME"

4. Jitter: Thundering Herd verhindern

Jitter ist die Ergänzung des exponentiellen Backoff in Shell-Skripten, die das Thundering-Herd-Problem löst. Wenn viele Instanzen desselben Skripts gleichzeitig starten — etwa nach einem Server-Neustart oder in einer horizontalen Skalierungsumgebung — und alle mit demselben Backoff-Schema nach einem Fehler wieder versuchen, treffen alle gleichzeitig auf den Zieldienst. Jitter fügt eine zufällige Komponente zur Wartezeit hinzu, die die Retry-Versuche zeitlich verteilt.

Die Implementierung von Jitter in Bash nutzt die Variable $RANDOM, die Werte von 0 bis 32767 liefert. Der einfachste Jitter: jitter=$(( RANDOM % max_jitter )) und wait=$(( backoff + jitter )). Das "Full Jitter"-Verfahren von AWS verwendet sleep $(( RANDOM % wait_seconds )) — die gesamte Wartezeit ist zufällig zwischen 0 und dem berechneten Backoff-Wert. Das "Equal Jitter"-Verfahren halbiert die Wartezeit und addiert eine zufällige Hälfte: $(( wait_seconds / 2 + RANDOM % (wait_seconds / 2) )). Für Shell-Skripte in Produktionsumgebungen ist Full Jitter oder Equal Jitter mit einem Backoff-Cap die empfohlene Strategie.

5. Der timeout-Befehl: Hängende Befehle abbrechen

Der timeout-Befehl aus GNU Coreutils ist das Standard-Werkzeug für Zeitlimits in Shell-Skripten. Die Syntax timeout DAUER BEFEHL [ARGS] führt den Befehl aus und sendet nach Ablauf der Dauer ein SIGTERM (Standard) oder ein anderes Signal (--signal). Mit --kill-after wird nach dem SIGTERM zusätzlich nach einer weiteren Wartezeit ein SIGKILL gesendet, falls der Prozess auf SIGTERM nicht reagiert. Der Exit-Code von timeout: 0 bei Erfolg, 124 wenn das Zeitlimit überschritten wurde, der originale Exit-Code des Befehls sonst.

Für Retry-Logik in Shell-Skripten ist die Kombination von timeout mit der Retry-Funktion besonders wertvoll: Jeder Versuch hat ein individuelles Zeitlimit, sodass ein einzelner hängender Versuch nicht die gesamte Retry-Sequenz blockiert. timeout 30 curl -sf ... bricht den curl-Aufruf nach 30 Sekunden mit Exit-Code 124 ab. Die Retry-Funktion behandelt Exit-Code 124 als normalen Fehler und macht weiter — inklusive Backoff. Das Zeitlimit pro Versuch sollte deutlich kürzer sein als das maximale Wartefenster der gesamten Retry-Sequenz.


#!/usr/bin/env bash
set -euo pipefail

# retry_with_backoff_jitter <max> <base> <max_wait> <cmd> [args...]
retry_with_backoff_jitter() {
  local max_attempts="$1"
  local base_wait="$2"
  local max_wait="$3"
  shift 3
  local attempt=1
  local wait_seconds="$base_wait"

  while (( attempt <= max_attempts )); do
    printf '[RETRY] Versuch %d/%d: %s\n' "$attempt" "$max_attempts" "$*" >&2

    # Each attempt gets its own timeout (30s)
    if timeout 30 "$@"; then
      return 0
    fi
    local exit_code=$?

    # Special handling for timeout exit code
    if [[ $exit_code -eq 124 ]]; then
      printf '[RETRY] Zeitlimit überschritten (30s)\n' >&2
    else
      printf '[RETRY] Fehlgeschlagen (Exit %d)\n' "$exit_code" >&2
    fi

    if (( attempt < max_attempts )); then
      # Equal jitter: half fixed, half random
      local half=$(( wait_seconds / 2 ))
      local jitter=$(( half > 0 ? RANDOM % half : 0 ))
      local actual_wait=$(( half + jitter ))

      printf '[RETRY] Backoff: %ds (base %ds + jitter %ds)\n' \
        "$actual_wait" "$half" "$jitter" >&2
      sleep "$actual_wait"

      # Exponential increase with cap
      wait_seconds=$(( wait_seconds * 2 ))
      (( wait_seconds > max_wait )) && wait_seconds="$max_wait"
    fi
    (( attempt++ ))
  done

  printf '[RETRY] Aufgegeben nach %d Versuchen\n' "$max_attempts" >&2
  return 1
}

# Wait for service availability on startup
wait_for_service() {
  local host="$1" port="$2"
  retry_with_backoff_jitter 10 1 30 \
    bash -c "echo > /dev/tcp/$host/$port" 2>/dev/null
  printf '[OK] Dienst %s:%s erreichbar\n' "$host" "$port" >&2
}

# Use in deployment context
wait_for_service "${DB_HOST:-localhost}" "${DB_PORT:-3306}"
wait_for_service "${REDIS_HOST:-localhost}" "${REDIS_PORT:-6379}"

6. Retry mit Timeout kombinieren

Die Kombination von Retry und Timeout in Shell-Skripten erfordert zwei separate Zeitgrenzen: das Timeout pro Versuch (wie lange ein einzelner Befehl laufen darf) und das Gesamt-Timeout für die gesamte Retry-Sequenz (wie lange insgesamt auf Erfolg gewartet wird). Ohne Gesamt-Timeout kann eine Retry-Sequenz mit 10 Versuchen und maximalem Backoff von 60 Sekunden theoretisch über 10 Minuten laufen — was in einer CI-Pipeline zu einem Job-Timeout führt, bevor die Retry-Sequenz selbst aufgibt.

Die Implementierung eines Gesamt-Timeouts für Retry in Shell-Skripten nutzt SECONDS — die eingebaute Bash-Variable, die die Sekunden seit dem Start der aktuellen Shell-Session zählt. Zu Beginn der Retry-Sequenz speichert man start_time=$SECONDS. Vor jedem Versuch prüft man (( SECONDS - start_time >= total_timeout )) und bricht ab, wenn das Gesamt-Timeout überschritten ist. Alternativ wird die gesamte Retry-Sequenz selbst von einem äußeren timeout-Befehl umschlossen.

7. Idempotenz: Voraussetzung für sicheres Retry

Die technische Voraussetzung für Retry-Logik in Shell-Skripten ist Idempotenz: Der wiederholte Aufruf eines Befehls muss zu demselben Ergebnis führen wie ein einmaliger Aufruf. Nicht alle Befehle sind von Natur aus idempotent. Ein INSERT INTO-SQL ohne INSERT OR IGNORE oder ON CONFLICT DO NOTHING erzeugt bei Wiederholung einen Duplikat-Fehler. Ein mkdir ohne -p scheitert, wenn das Verzeichnis bereits existiert. Ein API-Aufruf, der eine Transaktion startet, muss bei Wiederholung prüfen, ob die Transaktion bereits abgeschlossen ist.

Für Retry in Shell-Skripten gilt: Idempotente Operationen können unbegrenzt wiederholt werden. Nicht-idempotente Operationen müssen entweder idempotent gemacht werden (durch entsprechende Flags oder Transaktions-IDs) oder dürfen nicht Teil einer Retry-Schleife sein. Die Prüfung auf Idempotenz ist kein technisches Problem der Retry-Logik selbst, sondern eine Anforderung an die Skript-Architektur, die vor der Implementierung der Retry-Schleife beantwortet werden muss.

8. Retry für HTTP-APIs: Statuscodes berücksichtigen

Bei HTTP-API-Aufrufen in Shell-Skripten ist Retry nicht pauschal für alle Fehlerfälle angebracht. HTTP-Statuscodes unterscheiden zwischen Fehlern, bei denen Retry sinnvoll ist, und solchen, bei denen er sinnlos oder schädlich ist. Statuscodes 500, 502, 503 und 504 sind transiente Serverfehler — hier ist Retry mit Backoff die richtige Strategie. Statuscode 429 (Too Many Requests) enthält oft einen Retry-After-Header mit der genauen Wartezeit — diese muss ausgelesen und verwendet werden. Statuscodes 400, 401, 403 und 404 sind permanente Fehler — hier ist Retry sinnlos und verbraucht nur Ressourcen.

Die Implementierung von HTTP-Retry in Shell-Skripten mit curl: curl -w "%{http_code}" gibt den HTTP-Statuscode am Ende der Ausgabe aus. Mit --silent --output /dev/null wird der Body unterdrückt, wenn nur der Statuscode interessiert. Eine Wrapper-Funktion prüft den Statuscode und entscheidet, ob Retry, sofortiger Abbruch oder Erfolg angebracht ist. Für produktive API-Aufrufe empfiehlt sich zusätzlich --max-time für curl (individuelles Timeout pro HTTP-Verbindung) und --retry-connrefused für automatisches Retry bei Verbindungsverweigerung.


#!/usr/bin/env bash
set -euo pipefail

# http_retry: Retry HTTP request with status-code-aware logic
# Usage: http_retry <max_attempts> <url> [curl args...]
http_retry() {
  local max_attempts="$1" url="$2"; shift 2
  local attempt=1
  local wait_seconds=1
  local max_wait=60

  while (( attempt <= max_attempts )); do
    local response http_code body
    # Capture both body and status code
    response=$(curl -sf --max-time 20 -w "\n%{http_code}" "$@" "$url" 2>&1 || true)
    http_code=$(printf '%s' "$response" | tail -1)
    body=$(printf '%s' "$response" | head -n -1)

    printf '[HTTP] Versuch %d/%d — Status: %s\n' "$attempt" "$max_attempts" "$http_code" >&2

    case "$http_code" in
      2??)
        # Success
        printf '%s' "$body"
        return 0
        ;;
      429)
        # Rate limited — respect Retry-After header if present
        local retry_after
        retry_after=$(curl -sI --max-time 5 "$url" 2>/dev/null \
          | grep -i "Retry-After:" | awk '{print $2}' | tr -d '\r' || echo "$wait_seconds")
        printf '[HTTP] Rate-Limit — warte %ss (Retry-After)\n' "$retry_after" >&2
        sleep "$retry_after"
        ;;
      5??)
        # Transient server error — exponential backoff with jitter
        local jitter=$(( RANDOM % wait_seconds ))
        local actual_wait=$(( wait_seconds + jitter ))
        printf '[HTTP] Serverfehler %s — warte %ds (Backoff + Jitter)\n' \
          "$http_code" "$actual_wait" >&2
        sleep "$actual_wait"
        wait_seconds=$(( wait_seconds * 2 ))
        (( wait_seconds > max_wait )) && wait_seconds="$max_wait"
        ;;
      4??)
        # Permanent client error — no retry
        printf '[HTTP] Permanenter Fehler %s — kein Retry\n' "$http_code" >&2
        return 1
        ;;
      "")
        # Connection failed / timeout
        printf '[HTTP] Verbindungsfehler — warte %ds\n' "$wait_seconds" >&2
        sleep "$wait_seconds"
        wait_seconds=$(( wait_seconds * 2 ))
        (( wait_seconds > max_wait )) && wait_seconds="$max_wait"
        ;;
    esac
    (( attempt++ ))
  done

  printf '[HTTP] Alle %d Versuche fehlgeschlagen: %s\n' "$max_attempts" "$url" >&2
  return 1
}

# Usage
response=$(http_retry 5 "https://api.mironsoft.de/v1/deploy/status" \
  -H "Authorization: Bearer ${API_TOKEN:?Token fehlt}")
printf 'Response: %s\n' "$response"

9. Retry-Strategien im Vergleich

Verschiedene Retry-Strategien in Shell-Skripten sind für unterschiedliche Szenarien geeignet. Die Wahl hängt davon ab, wie häufig transiente Fehler auftreten, wie kritisch die Latenz ist und ob das Thundering-Herd-Problem relevant ist.

Strategie Wartezeit Thundering Herd Einsatz
Festes Intervall Konstant (z.B. 5s) Hoch Einfache Warteprüfungen
Exponentiell 1s, 2s, 4s, 8s… Mittel API-Calls, einzelne Instanzen
Exponentiell + Full Jitter random(0, 2^n) Minimal Viele parallele Instanzen
Exponentiell + Equal Jitter 2^n/2 + random(0, 2^n/2) Sehr gering Empfohlen für die meisten Fälle
Statuscode-abhängig Variabel nach HTTP-Code Gering HTTP-API-Aufrufe

Für die meisten Produktionsanwendungen von Retry-Logik in Shell-Skripten ist exponentielles Backoff mit Equal Jitter die beste Wahl: Es verteilt die Last gut, lässt sich einfach implementieren und hat ein vorhersagbares Verhalten. Festes Intervall ist nur für Szenarien geeignet, in denen genau bekannt ist, dass der Dienst nach einer festen Zeit wieder verfügbar sein wird — etwa beim Warten auf einen Service-Start nach einem Neustart. Statuscode-abhängiges Retry ist für alle HTTP-API-Aufrufe Pflicht, weil permanente Fehler nicht durch Wiederholung lösbar sind.

Mironsoft

Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur

Shell-Skripte, die transiente Fehler selbst lösen?

Wir implementieren Retry-Bibliotheken mit exponentiellem Backoff, Jitter und Timeout für eure Shell-Skripte — damit Deployments, Backup-Routinen und API-Aufrufe nicht mehr beim ersten transienten Fehler abbrechen.

Retry-Bibliothek

Wiederverwendbare Retry-Funktionen mit Backoff, Jitter und Timeout für eure Shell-Projekte

API-Integration

HTTP-Retry mit Statuscode-Logik und Retry-After-Header für Rate-Limited APIs

Resilienz-Review

Bestehende Shell-Skripte auf fehlende Retry-Logik und hängende Befehle analysieren

10. Zusammenfassung

Retry, Backoff und Timeout-Strategien in Shell-Skripten sind die Grundlage resilienter Automatisierung. Die drei Kernkomponenten — eine Retry-Schleife mit konfigurierter Maximalanzahl, exponentieller Backoff mit Jitter für verteilte Systeme und der timeout-Befehl für hängende Einzelaufrufe — lassen sich zu einer kompakten, wiederverwendbaren Bibliothek zusammenfassen. Einmal implementiert und in alle Skripte eingebunden, macht diese Bibliothek den Unterschied zwischen Skripten, die beim ersten Netzwerkflattern abbrechen, und solchen, die transiente Probleme selbstständig überwinden.

Für HTTP-API-Aufrufe ist zusätzlich die statuscode-abhängige Retry-Logik notwendig: Nicht jeder HTTP-Fehler sollte zu Retry führen. Permanente Fehler (4xx) niemals wiederholen. Transiente Fehler (5xx, Verbindungsfehler) mit exponentiellem Backoff wiederholen. Rate-Limits (429) mit dem Retry-After-Header respektieren. Idempotenz ist die Voraussetzung für jede Retry-Logik in Shell-Skripten — nicht-idempotente Operationen müssen explizit behandelt oder aus der Retry-Schleife ausgeschlossen werden.

Retry, Backoff und Timeout — Das Wichtigste auf einen Blick

Exponentielles Backoff

Wartezeit nach jedem Versuch verdoppeln. Cap bei max_wait (z.B. 60s). Typische Sequenz: 1, 2, 4, 8, 16, 30, 30… Sekunden.

Jitter

Equal Jitter: wait/2 + random(0, wait/2). Verhindert Thundering Herd bei parallelen Instanzen. $RANDOM für Integer-Zufallswerte in Bash.

timeout-Befehl

timeout 30 befehl — sendet SIGTERM nach 30s. Exit-Code 124 bei Timeout. --kill-after für SIGKILL nach weiterem Warten.

HTTP-Retry

5xx und Verbindungsfehler: Retry mit Backoff. 429: Retry-After-Header lesen. 4xx: kein Retry. curl -w "%{http_code}" für Statuscode-Auswertung.

11. FAQ: Retry, Backoff und Timeout in Shell-Skripten

1Was ist exponentielles Backoff?
Wartezeit verdoppelt sich nach jedem Fehler: 1s, 2s, 4s, 8s… mit Cap. Reduziert Last auf überlastete Dienste und Retry-Flut.
2Thundering Herd und Jitter?
Viele Clients treffen gleichzeitig auf den Dienst. Jitter verteilt Versuche zeitlich: wait/2 + random(0, wait/2) für Equal Jitter.
3timeout-Befehl in Bash?
timeout 30 befehl — SIGTERM nach 30s, Exit-Code 124 bei Timeout. --kill-after=5 für SIGKILL als Fallback.
4Jitter in Bash implementieren?
$RANDOM liefert 0–32767. Equal Jitter: $(( wait/2 + RANDOM % (wait/2) )). Full Jitter: $(( RANDOM % wait )).
5Welche HTTP-Codes für Retry?
Retry: 429 (Retry-After lesen), 500/502/503/504. Kein Retry: 400/401/403/404 — permanente Fehler werden nicht durch Wiederholung gelöst.
6Was ist Idempotenz?
Mehrfaches Ausführen gibt dasselbe Ergebnis wie einmaliges. Voraussetzung für sicheres Retry — nicht-idempotente Operationen aus Retry-Schleifen ausschließen.
7Retry mit Gesamt-Timeout?
start_time=$SECONDS am Anfang. Vor jedem Versuch: (( SECONDS - start_time >= total_timeout )) && break. Oder gesamte Retry-Sequenz in timeout wrappen.
8set -e und Retry-Schleife?
if "$@"; then return 0; fi — der if-Kontext verhindert set -e-Abbruch. Niemals || true innerhalb der Retry-Funktion, wenn der Exit-Code ausgewertet werden muss.
9Retry-After-Header auslesen?
curl -sI URL | grep -i 'Retry-After:' | awk '{print $2}' | tr -d '\r' — gibt Sekunden-Wert zurück. sleep damit als Wartezeit nutzen.
10Wie viele Retry-Versuche?
Health-Checks: 3–5. Service-Start-Warten: 10–15. API-Aufrufe: 3–5 mit Backoff. Immer Gesamt-Timeout passend zur SLA definieren.