Linking Runbooks to Services in the Catalog
A runbook nobody can find during an incident is a runbook that does not exist. The fix is structural: attach the runbook to the service in the catalog so it is one click from the entity page the responder is already looking at. This how-to links runbooks to services via annotations, keeps them close to the code, and surfaces them on the entity overview. It is the link-out half of Incident & On-Call Workflows within Developer Experience & Self-Service Platforms.
Prerequisites
- Backstage 1.20+ with the catalog populated and services owned correctly.
- Runbooks authored as durable content — ideally TechDocs pages beside the service, per TechDocs & Documentation Pipelines, so they version with the code.
- Agreement on what a runbook must contain: symptoms, first checks, mitigation, and escalation.
- Write access to each service’s
catalog-info.yamlto add link annotations.
Exact Configuration
-
Add link annotations to the service so the runbook appears in the entity’s Links.
# catalog-info.yaml apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: payments links: - url: https://backstage.example.com/docs/default/component/payments/runbooks/high-latency title: Runbook — High Latency icon: docs spec: type: service owner: team-payments -
Keep runbooks in the service’s TechDocs so the link resolves to versioned content, not an external wiki page.
<!-- docs/runbooks/high-latency.md --> # Runbook: High Latency ## Symptoms p99 latency above 500ms for 5 minutes. ## First checks 1. Is an incident already open? Check the entity overview. 2. Recent deploy? Compare against the deploy timeline. ## Mitigation Roll back the last deploy; scale the read replicas. ## Escalation If unresolved in 15 minutes, page the on-call lead. -
Register the docs in
mkdocs.ymlso the runbook is built and reachable.# mkdocs.yml nav: - Runbooks: - High Latency: runbooks/high-latency.md
Validation
# Requires curl, jq
# 1. The service entity carries a runbook link
curl -s "${PORTAL_URL}/api/catalog/entities/by-name/component/default/payments" \
| jq '[.metadata.links[] | select(.title | test("Runbook"))] | length'
# Expected: >= 1
# 2. The linked runbook page actually resolves
curl -s -o /dev/null -w "%{http_code}\n" \
"${PORTAL_URL}/docs/default/component/payments/runbooks/high-latency/"
# Expected: 200
# 3. No service is missing a runbook link (governance sweep)
curl -s "${PORTAL_URL}/api/catalog/entities?filter=kind=component" \
| jq '[.[] | select(([.metadata.links // [] | .[].title] | any(test("Runbook"))) | not) | .metadata.name]'
# Expected: [] once every service has one
Edge Cases & Troubleshooting
| Symptom | Root Cause | Resolution |
|---|---|---|
| Runbook link 404s | Docs not built or path changed | Rebuild TechDocs; confirm the nav path matches the link |
| Link points at a stale wiki | Runbook lives outside version control | Move it into the service’s TechDocs so it versions with code |
| No runbook on a critical service | No coverage enforcement | Run the sweep in CI and fail on services missing a link |
| Responder opens the wrong runbook | Generic title, unclear scope | Title runbooks by symptom, e.g. “High Latency”, not “Runbook” |
| Runbook steps are out of date | Never exercised | Reference it in incident reviews so gaps get fixed |
Frequently Asked Questions
One runbook per service, or one per failure mode?
Per failure mode, titled by symptom. A responder arrives with a symptom — high latency, elevated errors, a failing dependency — and wants the matching playbook, not a wall of prose covering everything. Several short, well-titled runbooks beat one exhaustive document nobody reads under pressure.
Should runbooks live in the portal or in the paging tool?
Author them once in TechDocs beside the code so they version with the service, then link to them from wherever a responder starts — the entity page and, if your paging tool supports it, the alert itself. A single source of truth linked from many places avoids the drift you get when the same runbook is pasted into two systems.