Automated Rollback Triggers and Runbook Integration

The operational pain this page solves is slow, manual recovery: a bad deploy ships, error rates climb, and minutes pass while someone notices the graph, finds the runbook, and manually reverts. Automated rollback closes that gap — objective signals trigger an immediate, hands-off reversal to the last known-good version, and the incident runbook is created and routed to on-call automatically so humans arrive to a contextualized incident rather than a mystery. This page covers the trigger design, the rollback action, the runbook wiring, and the safeguards that stop automated recovery from turning into a flapping loop. It complements the deployment strategies that make rollback fast in the first place.


Prerequisites


How Automated Rollback Works Under the Hood

Automated rollback is a small control loop bolted onto the end of a deploy: observe, decide, act, notify.

  1. Triggers. Two signal classes drive rollback. Health triggers are binary — a readiness or smoke-test failure after cutover. Metric triggers are statistical — error rate or latency breaching a threshold over a window, the same signals that gate a canary analysis. Health triggers catch hard failures instantly; metric triggers catch degradations that a health check would pass.
  2. The rollback action. The reversal must target a specific last known-good version, not “whatever ran before.” With blue-green it is a router flip; with a rollout it is an abort; with an immutable-release platform it is re-pointing an alias. The action is fast precisely because the target is already warm.
  3. Runbook integration. The moment a rollback fires, an incident is opened with the trigger, the from/to versions, and a metric snapshot, then routed to on-call. Responders start with context instead of assembling it during the outage.
  4. Loop safeguards. A naive “redeploy previous” can oscillate between two bad versions. Pinning the target and capping consecutive rollbacks converts a potential flap into a clean halt-and-page.
Automated Rollback Control Loop After a deploy, health and metric triggers observe the new version. A breach fires a rollback to the last known-good version and simultaneously opens an incident and pages on-call. A loop guard counts rollbacks and, past a cap, halts deploys and pages instead of flapping. Deploy new version Triggers health · error rate · latency breach Rollback to last known-good flip / abort / re-alias Open incident · page on-call Loop guard cap consecutive → halt & page

Step-by-Step Implementation

Step 1 — Record the last known-good version before deploy

# Capture the currently-serving version so rollback has a specific, warm target.
LKG=$(kubectl get deploy app -o jsonpath='{.spec.template.spec.containers[0].image}')
echo "$LKG" > .last-known-good
echo "Recorded last known-good: $LKG"

Verification: .last-known-good contains the image reference currently in production, not a moving previous pointer.

Step 2 — Define the post-deploy triggers

#!/usr/bin/env bash
# scripts/rollback-triggers.sh — returns 0 (healthy) or 1 (rollback)
set -uo pipefail
URL="https://app.example.com"

# Health trigger: smoke test must pass.
curl -sf "$URL/health" | jq -e '.status == "ok"' >/dev/null || { echo "TRIGGER: health check failed"; exit 1; }

# Metric trigger: 5xx rate over the last 3 minutes must stay under 2%.
ERR=$(curl -sf "http://prometheus:9090/api/v1/query" \
  --data-urlencode 'query=sum(rate(http_requests_total{app="web",code=~"5.."}[3m]))/sum(rate(http_requests_total{app="web"}[3m]))' \
  | jq -r '.data.result[0].value[1] // "0"')
awk -v e="$ERR" 'BEGIN{ exit !(e > 0.02) }' && { echo "TRIGGER: error rate ${ERR} > 0.02"; exit 1; }

echo "Post-deploy signals healthy."

Verification: Against a healthy deploy the script exits 0; injecting 5xx traffic makes it exit 1 with the breached value. Threshold design is expanded in wiring rollback to error-rate and latency thresholds.

Step 3 — Execute the rollback and open an incident

# .github/workflows/deploy-with-rollback.yml (rollback job)
  verify-and-maybe-rollback:
    needs: deploy
    runs-on: ubuntu-latest
    steps:
      - name: Post-deploy verification
        id: verify
        run: bash scripts/rollback-triggers.sh
        continue-on-error: true

      - name: Roll back to last known-good
        if: steps.verify.outcome == 'failure'
        run: |
          LKG=$(cat .last-known-good)
          kubectl set image deploy/app app="$LKG"
          kubectl rollout status deploy/app --timeout=120s
          echo "Rolled back to $LKG"

      - name: Open incident and page on-call
        if: steps.verify.outcome == 'failure'
        run: |
          curl -sf -X POST https://events.pagerduty.com/v2/enqueue \
            -H 'Content-Type: application/json' -d @- <<JSON
          {
            "routing_key": "${{ secrets.PD_ROUTING_KEY }}",
            "event_action": "trigger",
            "payload": {
              "summary": "Auto-rollback: app deploy ${{ github.sha }} reverted",
              "severity": "error",
              "source": "ci-rollback",
              "custom_details": { "rolled_back_to": "$(cat .last-known-good)", "commit": "${{ github.sha }}" }
            }
          }
          JSON

Verification: A forced trigger failure reverts the image and creates a PagerDuty incident whose details name the from/to versions.

Step 4 — Add loop safeguards

# scripts/loop-guard.sh — cap consecutive automated rollbacks
COUNT_FILE=.rollback-count
COUNT=$(cat "$COUNT_FILE" 2>/dev/null || echo 0)
if [ "$COUNT" -ge 2 ]; then
  echo "HALT: $COUNT consecutive rollbacks — freezing deploys and paging a human."
  exit 2   # a distinct code the pipeline treats as 'stop, do not retry'
fi
echo $((COUNT + 1)) > "$COUNT_FILE"

Verification: After two consecutive rollbacks the guard exits 2 and the pipeline stops auto-retrying, paging instead.


Configuration Reference

Option Type Default Effect
Rollback target enum last-known-good SHA Pin to a specific version, never a moving previous pointer
Error-rate threshold float 0.02 5xx ratio over the window that triggers rollback
Analysis window duration 3m How long post-deploy signals are observed before deciding
Trigger mode enum automatic Automatic for objective signals; manual approval for ambiguous ones
Consecutive-rollback cap integer 2 Halts and pages after this many automated rollbacks
Incident severity enum error Severity routed to the on-call escalation policy

Integration with Upstream and Downstream Topics

Rollback is the recovery layer for the whole delivery system:

  • Upstream — deployment strategy. Blue-green and canary keep a warm target that makes rollback near-instant; an aborted canary is an automated rollback.
  • Upstream — feature flags. When a rolled-back version exposed a flagged feature, the flag kill switch must flip as part of the rollback action.
  • Sibling — cost. Automated rollback runs deploy jobs; attribute and control that with tracking CI/CD compute costs for platform teams.
  • Upstream — pipeline structure. The rollback job slots into a multi-stage pipeline as the final post-deploy gate.

Performance and Cost Impact

Metric Value Notes
Mean time to recovery < 2 min Dominated by rollout-status wait; near-instant for a blue-green flip
Detection latency 30 s–3 min Set by the metric window; health failures detect immediately
Extra deploy jobs per incident 1 The rollback re-runs the deploy action against the known-good image
False-rollback rate low with proper windows Under-sized windows cause noise-driven rollbacks; see the threshold guide
On-call toil near zero to recover Humans arrive to a contextualized incident, not a manual revert

The biggest lever on MTTR is keeping the rollback target warm — the deployment-strategy choice, not the rollback code, determines whether recovery is seconds or minutes.


Troubleshooting

Symptom: Rollback reverts to a version that is also broken

Cause: The target was “the previous deploy,” which was itself a bad release, so the rollback oscillates.

Fix: Pin the target to a specific last-known-good SHA recorded before the current deploy (Step 1), and add the consecutive-rollback cap (Step 4) so repeated failures halt rather than flap.

Symptom: Rollback fires constantly on healthy deploys

Cause: The metric window is too short or the threshold too tight, so normal traffic noise trips the trigger.

Fix: Widen the window to cover a meaningful request count and set the threshold relative to the baseline. Threshold sizing is covered in wiring rollback to error-rate and latency thresholds.

Symptom: On-call is not notified when auto-rollback fires

Cause: The incident step is gated on the wrong condition or the routing key is missing, so recovery happens silently.

Fix: Ensure the incident step shares the rollback’s if condition and that the routing key targets the correct escalation policy. Silent auto-recovery hides recurring failures.

Symptom: Health check passes but users still see errors

Cause: The health endpoint is too shallow — it returns 200 on process liveness without checking dependencies.

Fix: Make /health assert real dependencies (database and cache connectivity) and add a metric trigger so degradations that a shallow check misses still cause rollback.


Frequently Asked Questions

Should rollback be automatic or require human approval?

For objective, unambiguous signals — a failed post-deploy health check or an error-rate threshold clearly breached — rollback should be automatic, because every minute of human latency is added downtime. Reserve manual approval for ambiguous cases where reverting might be worse than holding (a data migration mid-flight, for instance). In all cases, notify on-call even when recovery is automatic, so a pattern of auto-rollbacks is visible rather than silently absorbed.

How do I prevent a rollback loop?

Two safeguards. First, pin the rollback target to a specific last-known-good version captured before the deploy, not a relative “previous deploy” that might itself be broken. Second, cap consecutive automated rollbacks (Step 4); once the cap is hit, freeze deploys and page a human instead of continuing to flap. Together these turn a potential oscillation between two bad versions into a clean stop with a human in the loop.

What belongs in a rollback runbook?

The trigger that fired (which check, what value), the versions rolled back from and to, a metric snapshot at the moment of rollback, links to the relevant logs and dashboards, and the follow-up owner. Automating the runbook’s creation — as the incident step does — means responders open the incident already holding the context they would otherwise spend the first ten minutes gathering, which is often the difference between a short blip and a prolonged outage.


← Back to CI/CD Pipeline Architecture & Fundamentals