Runbook Engineering

Write operational runbooks that enable anyone to respond to incidents. Covers runbook structure, decision trees, automated diagnostics, escalation paths, pre-computed resolution steps, and the patterns that reduce MTTR by making expert knowledge accessible to everyone on call.

Runbooks are the operational backbone of production systems. They encode the collective knowledge of teams into executable, repeatable sequences of actions that respond to known failure modes. Runbook Engineering is the practice of designing, building, and maintaining these runbooks—ensuring they are reliable, discoverable, and actionable across teams and environments. This matters most during incident response, onboarding, and regular maintenance, when consistency and speed are critical, and when the difference between a minor outage and a cascading disaster lies in how well the team follows the runbook.

Designing a Runbook

A runbook begins with a clear problem statement. It answers: *What is this runbook for? What does it do? When should it be run?* The best runbooks start with a single sentence: "This runbook restores service after a database replication lag exceeds 10 minutes."

Each runbook is a sequence of steps, each with a title, a description, and executable commands. Steps may be manual, automated, or hybrid. The structure of a runbook is typically a list of numbered steps, each containing:

Example:

1. Confirm database replication lag
   - Description: Verify that the primary database is lagging behind the replica.
   - Precondition: Database connection is available.
   - Commands:
     psql -c "SELECT now(), pg_current_wal_lsn(), pg_last_wal_receive_lsn(), 
             (pg_current_wal_lsn() - pg_last_wal_receive_lsn()) / 1024 AS lag_mb 
             FROM pg_stat_replication;"
   - Expected output: lag_mb > 10240
   - If lag_mb ≤ 10240, go to step 3.
   - If lag_mb > 10240, proceed to step 2.

Executing a Runbook

Execution begins with a trigger: a monitoring alert, a scheduled job, or a manual invocation. The runbook is executed either by a person (manual runbook) or by a system (automated runbook), with the same interface in both cases.

Manual Execution

In manual execution, a human follows the runbook step-by-step. The runbook is stored in a shared location—e.g., a Markdown file in a Git repository under runbooks/.

Each step is marked as complete upon verification. A common pattern is to use a checklist with checkboxes:

- [x] Step 1: Confirmed replication lag
- [ ] Step 2: Promote replica to primary
- [ ] Step 3: Update application configuration
- [ ] Step 4: Verify service health

The executor logs actions as they go:

2025-04-05T12:30:00Z: Promoting replica to primary...
2025-04-05T12:32:15Z: pg_ctl promote -D /var/lib/postgresql/data succeeded.
2025-04-05T12:33:00Z: Running health checks...
2025-04-05T12:34:20Z: All services healthy. Proceeding to next step.

Automated Execution

Automated runbooks are triggered by events and executed by a workflow engine. Common triggers:

The execution engine runs the runbook as a sequence of tasks. Each task is a step from the runbook, executed in order, with error handling.

Example of a runbook executed via a YAML-based workflow engine (like Argo Workflows or Tekton):

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  name: db-replication-lag-restore
spec:
  entrypoint: db-replication-lag-restore
  templates:
    - name: db-replication-lag-restore
      steps:
        - - name: confirm-replication-lag
            template: check-lag
        - - name: promote-replica
            template: promote-replica
            when: "{{steps.confirm-replication-lag.outputs.result}} > 10240"
        - - name: update-config
            template: update-config
            when: "{{steps.promote-replica.succeeded}}"
        - - name: verify-health
            template: verify-health
  templates:
    - name: check-lag
      container:
        image: postgres:15
        command:
          - sh
          - -c
          - |
            psql -h db-primary -U app_user -d app_db -c "
              SELECT now(), pg_current_wal_lsn(), pg_last_wal_receive_lsn(),
                     (pg_current_wal_lsn() - pg_last_wal_receive_lsn()) / 1024 AS lag_mb
              FROM pg_stat_replication;"
        outputs:
          result: "{{steps.check-lag.outputs.result}}"
    - name: promote-replica
      container:
        image: postgres:15
        command:
          - sh
          - -c
          - |
            pg_ctl promote -D /var/lib/postgresql/data &&
            echo "Promotion successful" > /tmp/promotion-success
    - name: update-config
      container:
        image: alpine:latest
        command:
          - sh
          - -c
          - |
            sed -i 's/primary: .*/primary: replica-01/g' /etc/app/config.yaml &&
            systemctl reload app-service
    - name: verify-health
      container:
        image: curlimages/curl:latest
        command:
          - sh
          - -c
          - |
            until curl -f http://app-service:8080/health; do
              echo "Waiting for service..." && sleep 5
            done
            echo "Service is healthy."

Structuring Runbooks for Discoverability

Runbooks must be easy to find. They live in a shared directory, version-controlled in Git. The root directory is runbooks/, and each runbook is a Markdown file with a .md extension.

Each runbook has a standard front matter:

---
title: "Restore Database Replication Lag"
description: |
  When the primary database falls behind its replica by more than 10MB, promote the replica to primary.
  This runbook handles the entire process from detection to validation.
tags:
  - database
  - high-availability
  - incident-response
  - postgres
trigger:
  - prometheus: "db_replication_lag_mb > 10240"
  - cloudwatch: "RDS/ReplicationLag > 10240"
  - manual: true
  - scheduled: "daily"
author: "sre-team"
created: "2025-01-10"
updated: "2025-04-05"
---

Runbooks are discoverable via:

Maintaining Runbooks

Runbooks evolve. A runbook that was correct when first written often fails when applied in a new environment or under different load.

Versioning and Change Tracking

Each runbook is versioned in Git. Changes are tracked via pull requests. A change to a runbook includes:

Example PR description: > Fix replication lag restore: add pg_wal_lsn check before promotion > > - Before: only pg_last_wal_receive_lsn() was used. > - After: now also checks pg_current_wal_lsn() to detect if the lag is truly increasing. > - Fixes: https://github.com/org/app/issues/1234 > - Test: ran against staging with 15-minute lag simulation.

Reviewing Runbooks

Runbooks are reviewed during post-mortems. When an incident occurs, the team evaluates whether the runbook was followed correctly and whether it was sufficient.

Key questions during review:

A common failure mode: the runbook assumes psql is available but does not specify the connection string. The runbook fails when the database server is behind a firewall or when credentials are missing.

Standardizing Runbook Structure

To avoid drift, teams adopt a standard runbook template. The template includes:

Example appendix:

### Known Issues

- **Error: `psql: could not connect to server`**
  - Cause: `PGHOST` environment variable not set.
  - Fix: Set `export PGHOST=db-primary` before running the command.

- **Error: `pg_ctl promote: command not found`**
  - Cause: `pg_ctl` not in `PATH`.
  - Fix: Add `/usr/pgsql-15/bin` to `PATH`.

- **Error: `curl: (7) Failed to connect to app-service port 8080: Connection refused`**
  - Cause: Application service not running.
  - Fix: Run `systemctl start app-service` before health check.

Handling Runbook Failures

Runbooks fail silently. A command succeeds but produces incorrect output. A step is marked complete even when the action did not take effect.

Common Failure Modes

Idiomatic Runbook Patterns

  for i in {1..5}; do
    if curl -f http://app-service:8080/health; then
      echo "Success"
      break
    fi
    echo "Attempt $i failed. Waiting 10 seconds..."
    sleep 10
  done
  2. Check service health
     - If status code is 200, go to step 4.
     - If status code is 503, go to step 3.
  - [ ] Step 1: Update config on all servers (parallel)
  - [ ] Step 2: Restart app services (parallel)
  - [ ] Step 3: Run database migration (parallel)

Integrating Runbooks into Operations

Runbooks are not isolated documents. They are integrated into the operational workflow.

Runbooks are also used in change management: before a release, the team runs a "release checklist" runbook that verifies all prerequisites are met.

Conclusion

Runbook Engineering is the craft of turning operational knowledge into executable, maintainable, and discoverable procedures. It is not about writing documentation—it is about building systems that ensure consistency, reduce cognitive load, and make teams resilient. The runbook is the place where theory meets practice, and where the difference between a good and a great operations team is written in plain text and executed in real time.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.