Development

From Alerts to Runbooks: Wiring a Pager That Points to a Fix

Antonio Evans10 min read

A laptop screen in a dim room shows a monitoring alert popup.
Key points

Key points

  1. 1Every page should answer three questions in its first line: which service, what user-visible symptom, and what to check first.
  2. 2Runbook URL and dashboard URL belong in dedicated alert fields, not buried in a paragraph of text.
  3. 3Route by service and severity to one on-call rotation, and keep a separate low-urgency channel for everything that is not a page.
  4. 4Test the full path by firing the alert into a staging channel and watching a responder act without opening a search tab.

Start from the symptom, not the metric

The single most useful change a small team can make is to alert on what users feel rather than on what the machine reports. OpenTelemetry's observability primer draws the line clearly: an SLI is a measurement of a service's behavior from the user's perspective, and an SLO is that SLI attached to business value (OpenTelemetry observability primer). A page that says "node CPU above 80 percent" describes a cause candidate, not a symptom. A page that says "checkout error rate above 2 percent for 5 minutes" describes the user's pain and gives the responder a north star.

This distinction also fixes the routing problem. Metrics are shared infrastructure; symptoms have owners. When the alert names the symptom, the question "who should be woken" answers itself, because exactly one team owns whether checkout succeeds.

The four golden signals give you a ready vocabulary for those symptoms: latency, traffic, errors, and saturation, measured at the boundary of each user-facing service (Google SRE, Monitoring Distributed Systems). For a small team, three of them are usually enough to page on: error rate, latency on successful requests, and saturation of the one resource that fails first (a connection pool, a queue, or a database). Traffic is context, not a page.

One trap worth naming: never average latency across successful and failed requests. Errors fail fast, so a spike in fast 500s pulls the average down and makes a broken service look healthy. Track the latency of successful requests only, and use a high percentile rather than a mean (the golden signals guide).

Write the alert so it names the service, the symptom, and three checks

Treat the alert body as a template with fixed slots. A responder reads the first line at 3 a.m. and decides in five seconds whether to roll back, scale, or read further. If the first line does not contain everything they need, the alert has failed before it has started.

A workable slot layout:

SlotWhat goes in itBad exampleGood example
ServiceThe owning service, in the same spelling used everywheresvc_2checkout-api
SymptomThe user-visible failure and the measured windowerrors uperror rate 4.1% over 5m (SLO 99.5%)
First checkThe single highest-value thing to inspectlook at logsrollback status: last deploy 12m ago
Second checkThe next most likely cause(missing)db pool: 180/200 connections
Third checkThe last step before escalation(missing)upstream payment provider status page
RunbookA URL field, not prose"see wiki"runbook_url=https://...
DashboardA URL field scoped to the symptomdashboard_url=https://...dashboard_url=https://...?var-service=checkout-api

The structure matters because responders scan rather than read under stress, and because structured fields are searchable and groupable later. Structured logging guidance makes the same point about logs generally: a predictable shape lets tools filter by field instead of parsing fragile text (SigNoz on structured logging, New Relic on structured logging).

Here is a payload that implements the table. It uses a generic JSON shape that most paging tools can consume through a webhook or a templated integration:

{
  "service": "checkout-api",
  "severity": "page",
  "symptom": "error_rate 4.1% over 5m (SLO 99.5%)",
  "checks": [
    "rollback status: last deploy 12m ago, GET /deploys?service=checkout-api",
    "db pool saturation: 180/200 connections on checkout-db",
    "payment provider status: check upstream status page"
  ],
  "runbook_url": "https://runbooks.internal/checkout-api/error-rate",
  "dashboard_url": "https://dash.internal/d/checkout?var-service=checkout-api&var-window=30m",
  "dedupe_key": "checkout-api:error_rate"
}

Three details make this template earn its keep. First, the checks array is ordered, shortest path first, and each item says how to check, not just what to check. Second, dedupe_key collapses repeat notifications for the same symptom, which is where most noise comes from. Third, both URLs are deep-linked and pre-filtered, so the responder lands on the right view instead of a generic home dashboard.

Choose a symptom vocabulary and stick to it across services. If one team pages on error_rate and another on failures, nobody can write a shared query, and nobody can route on the field. A shared schema is exactly the discipline that structured logging field guides recommend for production systems, where inconsistent field names across services break cross-service queries (OpenObserve's structured logging field guide).

Package the runbook as a checklist, not an essay

Runbooks fail when they are written as narrative. During an incident, a responder will not read four paragraphs of background; they will scroll to the numbered actions, or they will give up and start searching. Write the runbook the way you would write the alert body, with the same slots.

A runbook that fits on one screen and works:

  1. Confirm the alert is real. Open the dashboard in the alert. Does the error rate on the dashboard match the alert? If not, fix the alert, not the service.
  2. Check the rollback status. If a deploy landed in the last 30 minutes, roll back first and investigate after. Most sharp onset failures are deploy-shaped.
  3. Check the dependency that fails first. For checkout, that is the database connection pool and the payment provider. Numbers and links, not descriptions.
  4. Decide whether to escalate. If step 2 or 3 does not explain the symptom within 10 minutes, page the secondary and open an incident channel.
  5. Record what you did, in the incident channel, as you do it. This is the raw material for the postmortem.

Every step should have one of three outcomes: act, skip, or escalate. If a step can end in "consult the architecture diagram," it is not a runbook step, it is a reading assignment.

Two consistency checks keep runbooks from rotting. First, the runbook must name the same dashboard the alert links to; a mismatch sends responders to two different views and destroys trust in both. Second, after every incident, the person who was paged updates the runbook before closing the incident. A runbook that was followed and found wrong is a gift: it tells you exactly which step to fix.

Runbook discipline pairs naturally with instrumenting before you need it. An application is properly instrumented, in the OpenTelemetry framing, when developers do not need to add more instrumentation to troubleshoot an issue because they already have the signals they need (OpenTelemetry observability primer). The same test applies to the runbook: if the responder has to add a query to investigate, the runbook is missing a step.

Route by service and severity, not by person

Small teams often start with alerts going to a shared channel and a hope that someone looks. That works until the first weekend. The fix is unglamorous: define two severity levels and route each to exactly one place.

SeverityDefinitionDestinationResponse expectation
pageAn SLO for a user-facing service is burning fast, or the service is downOn-call rotation for the owning service, via pagerAcknowledge and begin the runbook
ticketA slow burn, a saturation warning, or a non-user-facing failureTeam channel or backlogSame business day
infoDeployments, scaling events, configuration changesLog or audit channelNo response

The most common routing mistake is paging on causes. Saturation is a leading indicator, and a saturation warning at 80 percent CPU on a service that regularly spikes during a batch job will page the team every night for expected behavior (alert fatigue and SLO-based alerting). Route saturation to a ticket by default, and promote it to a page only when it coincides with a user-facing symptom.

The second mistake is routing by person. Route by service. A rotation that follows the service, not the individual, means that whoever is on call has the context and the runbook, and it means developer vacation does not create an alerting hole.

For a small team, one primary rotation with an escalation policy is enough. An escalation that never dead-ends matters more than a clever rotation model: if the primary does not acknowledge within the configured window, the alert moves to the secondary, then to the engineering lead (on-call best practices). Test the escalation path by hand at least once, because an escalation rule that was never exercised is a guess.

Tune what pages with burn rates and short windows

Paging on "the SLO has been breached" is too slow and too noisy at the same time. The SRE Workbook's alerting chapter compares several strategies and lands on multi-window, multi-burn-rate alerting as the most effective, because it balances precision, recall, detection time, and reset time simultaneously (Google SRE Workbook, Alerting on SLOs).

The mechanics are worth knowing even if your tool implements them for you. A burn rate of 1 means you are consuming the error budget at exactly the sustainable pace; a burn rate of 14.4 over a 1 hour window means you will exhaust a 30 day budget in about two days. The standard pairing uses one long window to confirm the problem is real and one short window to confirm it is still happening, so the alert fires fast and resets fast. A slow burn gets a ticket rather than a page.

Worked example. Suppose checkout has a 99.5 percent availability SLO over 30 days, which allows about 3 hours 36 minutes of bad time in the window. A page fires when the burn rate is 14.4 over a 1 hour window and at least 14.4 over a 5 minute window. A brief blip that resolves in two minutes will trip the short window but not the long one, so nobody is woken. A real outage trips both, and the page arrives within roughly five minutes.

If you are not ready to compute burn rates, a simpler starting point still beats CPU thresholds: page on the ratio of bad to total events over a 5 minute window, with a minimum traffic floor so that three failures at 2 a.m. on low traffic do not page anyone. Low-traffic periods are a recognized weakness of error-budget alerting, and Google's guidance discusses them directly (Google SRE Workbook, Alerting on SLOs).

The data behind all of this comes from your telemetry pipeline. OpenTelemetry is a vendor-neutral framework for generating, collecting, and exporting traces, metrics, and logs, so the same signals can feed whichever backend you page from (What is OpenTelemetry). It is deliberately not a backend itself, which means the alerting layer is a separate decision you get to make on its own merits.

A note for teams building AI features alongside their services. Alerting on prompt and completion latency or on inference error rates needs the same three-part discipline as any other service: named symptom, three checks, and a link to the relevant view. If your models come from several providers and you want one place to see latency and errors across them, a single API key for text, image, video, audio, and embedding models simplifies the observability surface considerably. That is what Quanta Inference offers, with OpenAI-compatible endpoints and prepaid pricing in US dollars: Quanta Inference.

Test the path before you trust it

An untested alert is a hypothesis. The last step in wiring a pager is proving that a responder can act on the page without searching.

A test protocol that takes under an hour and catches most failures:

# 1. Fire a synthetic alert into the staging channel with realistic fields
curl -X POST "$PAGER_STAGING_WEBHOOK" \
  -H "Content-Type: application/json" \
  -d @checkout-error-rate.json

# 2. Check that the escalation policy would have paged the right person
#    (use a "do not actually call" flag or a shadow policy where supported)

# 3. Check that both URLs resolve and are pre-filtered to the right service
curl -sI "$RUNBOOK_URL" | head -n 1
curl -sI "$DASHBOARD_URL" | head -n 1

Then hand the page to a responder who did not write it and ask them to follow the runbook out loud. What you are looking for:

  • The first line tells them which service and which symptom, with no ambiguity.
  • The three checks are ordered and each one tells them how to check.
  • Both links open the right view, filtered to the service and the relevant window, on the first click.
  • The runbook does not contradict the alert.
  • The escalation path is obvious if the first three checks come up empty.

Run this drill every time a runbook changes, every time a service is added, and after any incident where the responder had to improvise. Record the time from page to first meaningful action in the incident log; the trend is a better health metric for your on-call than the count of alerts.

If you want the same discipline applied to the tooling around the alert, from a first dashboard to the internal site that hosts the runbooks, describing the problem to a builder is often faster than wiring it yourself. Quanta pairs you with a specialist who builds the site, app, or document with you: Quanta.

Common failure modes and how to spot them early

Most pager programs fail in a small number of predictable ways. The table below is a checklist you can run against your own setup.

Symptom in your programLikely causeFix
Responders open a search tab before actingAlert body lacks symptom, checks, or linksEnforce the three-slot template
The same incident produces 40 notificationsNo dedupe key, no groupingAdd a stable dedupe key per service and symptom
Alerts get silenced and a real outage slipsPaging on causes and infrastructure thresholdsMove to symptom-based, SLO burn alerts
Nobody updates the runbookNo step in the incident close-outMake a runbook edit part of resolving the incident
A new service pages the wrong teamRouting by person or by default rotationRoute by service tag from day one
Fill this row with your own failure(diagnose it)(fix it before the next on-call shift)

Two diagnostics tell you where to focus. Count the pages that required no action over a rotation; if that number is high, the problem is precision, and the fix is usually tighter windows or moving causes to tickets. Count the incidents where the first action was not in the runbook; if that number is high, the problem is the runbook, and the fix is to write step one and step two with numbers and links.

Neither diagnostic requires new tooling. Both require someone to look at last week's pages and write down what happened, which is the cheapest reliability work available to a small team.

FAQ

FAQ

What is the minimum viable alert body for a small team?

One line naming the service and the user-visible symptom with its measurement window, followed by an ordered list of three checks that each say how to verify. Two URL fields, one for the runbook and one for a pre-filtered dashboard, complete it. Anything beyond that is commentary and can live in the runbook itself.

Should we page on SLO burn rate or on simple thresholds?

Burn-rate alerting is more precise because it pages in proportion to how fast the error budget is being consumed, which is what the SRE Workbook recommends after comparing several strategies (Google SRE Workbook, Alerting on SLOs). That said, a symptom-based ratio alert over a short window with a traffic floor is a fine starting point. The rule that matters more than the formula is that you page on user-visible symptoms, not on CPU.

How do we keep runbooks from going stale?

Make a runbook edit part of closing an incident, and require the person who was paged to make it. Also require that the runbook and the alert link to the same dashboard, and check that during the drill. A runbook nobody has followed in months is a document, not a runbook.

Where do logs, metrics, and traces fit into the page?

They are the evidence the responder consults after reading the three checks, not the page itself. Metrics tell you a symptom is happening, traces tell you where in the request path it is happening, and logs tell you why, which is the split the observability literature uses consistently (the OpenTelemetry observability primer). The alert's job is to tell the responder which of those to open first.

How many pages per shift is too many for a small team?

The SRE Workbook recommends no more than two or three actionable incidents per shift as a sustainable baseline (on-call best practices). If you are consistently above that, treat it as a routing and runbook problem before you treat it as a staffing problem. Most excess pages trace back to paging on causes instead of symptoms, and to a missing dedupe key.

Sources

Sources

  1. Observability primer - OpenTelemetry
  2. Chapter 6 - Monitoring Distributed Systems - Google SRE
  3. The Four Golden Signals of Monitoring: A Complete Guide (2026)
  4. What Is Structured Logging? Benefits and Tips | SigNoz
  5. Structured logging: What it is and why you need it - New Relic
  6. Structured Logging in Production: Best Practices for Scalable Systems
  7. Alert Fatigue: Fix Noisy Alerts with SLO-Based Alerting
  8. On-call best practices: handoffs, schedules, and alert fatigue
  9. Chapter 5 - Alerting on SLOs
  10. What is OpenTelemetry?
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles