SLOs in Practice: Beyond the Math
SLOs in Practice: Beyond the Math
The SLO conversation in most organizations goes one of two ways. Either it stays theoretical — engineers understand the concepts, there are some dashboards, but nobody actually changes behavior based on the error budget. Or it gets implemented too rigidly, producing alert fatigue and resentment because the numbers don’t reflect what users actually experience.
Getting SLOs to work requires more than correct math. It requires alert designs that page at the right time, error budget policies that have teeth, tooling that keeps the overhead low, and — hardest of all — organizational buy-in that makes the whole thing worth doing.
This post covers all of it: the alerting math that actually works, the tools that automate the boilerplate, and the organizational patterns that make SLOs stick.
A Brief Grounding in the Concepts
If you’re already comfortable with SLIs, SLOs, and error budgets, skip this section. If not, a quick grounding:
SLI (Service Level Indicator): a quantitative measure of service behavior. The most common ones:
- Availability: fraction of requests that succeeded (
good_requests / total_requests) - Latency: fraction of requests completing under a threshold (
requests_under_100ms / total_requests) - Throughput: requests processed per second (less common for SLOs)
- Error rate: fraction of requests returning errors
SLO (Service Level Objective): a target for the SLI over a time window. “99.9% of requests succeed over a 30-day rolling window.”
Error budget: the amount of unreliability the SLO permits. For a 99.9% SLO over 30 days: (1 - 0.999) × 30 × 24 × 60 = 43.2 minutes of downtime. That’s your budget to spend on deployments, incidents, and maintenance.
SLA (Service Level Agreement): a contract with consequences. SLAs are usually a floor below your SLO — if you commit to 99.9% SLO internally, your SLA might be 99.5%.
Why Simple Threshold Alerts on SLOs Fail
The first instinct when you have an SLO is to alert when you breach it. “Alert if error rate > 0.1% in the last 5 minutes.” This seems reasonable but has two fatal problems:
Too noisy: a 5-minute spike that costs you 5 minutes of error budget out of 43 minutes doesn’t warrant waking anyone up at 2 AM. You’d burn through on-call patience long before the SLO is at risk.
Too slow: if you’re burning error budget slowly (0.2% errors, twice the allowed rate), a 5-minute alert window won’t catch it until you’ve already spent half your budget.
The Google SRE book introduced a better framework: burn rate alerting.
Burn Rate Alerting
The burn rate is how fast you’re consuming error budget relative to the rate at which it replenishes. A burn rate of 1 means you’re consuming budget at exactly the rate it regenerates — you’ll end the SLO window exactly at the objective. A burn rate of 2 means you’re consuming twice as fast as it replenishes — you’ll exhaust the budget halfway through the window.
For a 30-day SLO with a 0.1% error budget (99.9% target):
- Total error budget: 43.2 minutes
- Budget per hour: 43.2 / (30 × 24) = 0.06 minutes = 3.6 seconds/hour
If your error rate is 1% (10× the allowed 0.1%), your burn rate is 10. You’ll exhaust the entire 30-day budget in 3 days.
The alert threshold becomes: at this burn rate, how long until the budget is exhausted?
time_to_exhaustion = budget_remaining / burn_rate
A burn rate of 14.4 means the budget exhausts in:
30 days / 14.4 = 2.08 days ≈ 50 hours
That’s worth paging someone. A burn rate of 1 is not.
Multi-Window Multi-Burn-Rate (MWMB) Alerting
The Google-recommended approach uses two alert windows at each of two burn rates:
| Severity | Burn Rate | Short Window | Long Window | Budget Consumed |
|---|---|---|---|---|
| Page (critical) | 14.4× | 1 hour | 5 minutes | 2% in 1h |
| Page (critical) | 6× | 6 hours | 30 minutes | 5% in 6h |
| Ticket (warning) | 3× | 1 day | 2 hours | 10% in 1d |
| Ticket (warning) | 1× | 3 days | 6 hours | 10% in 3d |
Why two windows per alert? The short window detects the problem quickly; the long window provides confidence that it’s real and not a brief spike. You alert only when both windows show the burn rate threshold exceeded. This dramatically reduces false positives.
Why 5 minutes + 1 hour for the critical alert?
- 1-hour window catches sustained elevated error rates
- 5-minute window ensures the condition is still active right now (avoids alerting on a problem that resolved itself)
Prometheus Alert Rules
|
|
Latency SLOs
Latency SLOs use histograms. The SLI is “fraction of requests completing under X ms”:
|
|
Defining Good SLIs
The math only helps if your SLI actually measures what users care about. Common mistakes:
Measuring the wrong thing: if your load balancer returns 200 OK with an error message in the body, a simple status != 5xx SLI will miss those errors. Measure at the right layer.
Ignoring partial availability: if 20% of users are hitting a bad shard but the overall error rate is low, your SLO won’t fire. Consider per-region, per-shard, or per-user-cohort SLIs for services with sharded data.
Using averages for latency: a p50 latency SLO misses tail latency. Use a histogram-based SLI: “99th percentile of requests complete in under 1 second.” If you can’t use histograms, at minimum use p99.
Not excluding maintenance windows: planned downtime should be excluded from error budget consumption. Keep a configuration that marks maintenance periods and excludes them from SLI calculation.
Including health check traffic: if your monitoring pings your service every 10 seconds and you have very little real traffic, health check traffic dominates and makes your SLI look artificially good or bad depending on whether the checks are included correctly.
The SLI Specification Format
A clean way to document SLIs before implementing them:
|
|
Tools: Sloth and Pyrra
Writing and maintaining SLO alert rules by hand is error-prone and tedious. Two tools automate the generation of MWMB alert rules from a higher-level spec.
Sloth
Sloth generates Prometheus alert rules and recording rules from an SLO spec. You define the SLO once; Sloth generates the multi-window burn rate alerts automatically.
|
|
Generate the Prometheus rules:
|
|
Sloth also generates recording rules that pre-compute error budget metrics — critical for dashboards, since computing burn rates over long windows in real-time is expensive.
Pyrra
Pyrra is similar to Sloth but adds a built-in UI for visualizing SLO status and error budget consumption. It runs as a Kubernetes operator:
|
|
|
|
Pyrra’s UI shows each SLO’s current status, error budget remaining as a percentage and in time, and historical burn rate trends.
Choosing Between Sloth and Pyrra
| Sloth | Pyrra | |
|---|---|---|
| Interface | CLI / YAML | Kubernetes operator + UI |
| UI | No (use Grafana) | Built-in |
| Multi-cluster | Manual | Native via operator |
| Alertmanager integration | Via generated rules | Via generated rules |
| Best for | GitOps, non-Kubernetes | Kubernetes-native |
The Error Budget Policy
An error budget is only useful if it changes behavior. Without a policy, a team with 0% error budget remaining behaves identically to a team at 50% — there are no consequences or guardrails.
An error budget policy answers three questions:
- What does the team commit to do while the budget is healthy?
- What happens when the budget is depleted?
- What happens when the budget is critically low (< 25%)?
Sample Error Budget Policy
|
|
The specific thresholds matter less than the policy being written down, agreed upon, and actually enforced. Teams that have a clear policy can make rational decisions about risk. Teams without one fight the same arguments every sprint.
Grafana Dashboards for SLOs
Standard dashboard layout for an SLO:
Row 1: Status panel
- Current SLO status (Met / At Risk / Breached) — large colored indicator
- Error budget remaining (as percentage and minutes/hours)
- Current burn rate
Row 2: Error budget burn over time
- Graph of error budget remaining over the 30-day window
- Annotated with incidents and deployments
Row 3: SLI details
- Error rate over time (with SLO threshold line)
- Latency percentiles (p50, p90, p99) over time
- Request volume over time
Row 4: Alert status
- Table of active SLO alerts with burn rate and severity
Grafana dashboard as code (using the Grafana Terraform provider or a dashboard JSON):
|
|
The recording rules generated by Sloth or Pyrra make these dashboard queries fast — they pre-compute the rolling window metrics so dashboards don’t need to scan weeks of raw data.
Organizational Challenges
The technical implementation is the easy part. SLOs fail for organizational reasons far more often than technical ones.
“Who owns the SLO?”
SLOs only work when a team owns both the reliability of the service and the error budget policy. Shared services with unclear ownership don’t get reliable — they get argued about.
The pattern that works: SLO ownership mirrors service ownership. The team that develops and operates a service owns its SLOs. Platform/SRE teams can advise on SLI selection and tooling, but the product team owns the numbers and the budget.
“The SLO doesn’t reflect user experience”
This is the most common technical complaint, and it’s often valid. An SLO based on HTTP 5xx rate looks fine while users are experiencing slow page loads, timeouts handled with retries, or degraded functionality that returns 200 OK.
Fixes:
- Add a user-journey SLO that measures end-to-end critical paths, not just the API layer
- Add synthetic monitoring (Grafana k6, Playwright in a cron) that simulates real user actions
- Collect real user monitoring (RUM) data and use it to calibrate SLOs
“The error budget is always green, so nothing changes”
If the error budget never gets threatened, the SLO is too loose. This feels safe but means the SLO provides no signal. The error budget should feel meaningfully constrained — if a team deploys 10 times a week, they should feel some budget pressure from normal operations.
Use actual historical data to set SLOs. Look at your last 90 days of error rate. If you’ve been running at 99.97% availability, setting the SLO at 99.9% means the budget is nearly always full and has no behavioral effect. Set it at 99.95% instead.
“We keep getting paged on things that aren’t real problems”
This is the burn rate calibration problem. Common causes:
- SLI measures something users don’t care about (health check traffic, internal retries)
- Burn rate thresholds are too sensitive for actual traffic patterns
- No
forduration on alerts (single-minute spikes trigger pages)
The fix is iteration. Treat alert configuration like code — review it in retrospectives, track false positive rates, and tune aggressively in the first few months.
“Product keeps pushing features and ignoring the error budget”
The error budget policy only works if leadership enforces it. If a VP can override the deployment freeze because “this feature has to ship,” the policy is theater.
This is primarily a management problem, not a technical one. Escalation paths and executive sponsorship of reliability work have to be established before an incident, not during one.
Getting Buy-In: Start Small
The pattern that works for introducing SLOs into skeptical organizations:
- Pick one service with a motivated team — don’t roll out organization-wide on day one
- Set obviously achievable SLOs initially — the goal is building the instrumentation and process, not setting an aggressive bar
- Focus on making dashboards useful — teams need to see value before they’ll accept constraints
- Let the first error budget burn happen naturally — don’t engineer a crisis, but don’t paper over an incident that consumes budget either
- Hold the retrospective — after the first budget-consuming incident, walk through what happened with the policy in hand
- Tighten the SLO after 3–6 months — once the team trusts the instrumentation
The teams that implement SLOs successfully almost always describe it as “the first time we had a shared language for reliability conversations with product management.” That’s the real value — not the math, but the ability to say “we’ve consumed 40% of our error budget this month, so the next deployment needs extra scrutiny” in a meeting and have everyone understand what that means.
Quick Reference
Burn Rate → Time to Exhaustion (30-day SLO)
| Burn Rate | Hours Until Budget Exhausted |
|---|---|
| 1× | 720 hours (30 days) |
| 2× | 360 hours (15 days) |
| 3× | 240 hours (10 days) |
| 6× | 120 hours (5 days) |
| 14.4× | 50 hours (~2 days) |
| 36× | 20 hours |
| 72× | 10 hours |
| 720× | 1 hour |
Standard MWMB Alert Thresholds
|
|
Minimum Traffic for SLO Reliability
SLOs on low-traffic services produce noisy signals. A single error on a service receiving 1 request/minute moves the error rate from 0% to 100% for that minute. Rule of thumb: you need at least 100–1,000 events per measurement window for the SLI to be statistically meaningful.
If traffic is too low, consider:
- Synthetic monitoring as the SLI source
- Longer measurement windows (24-hour windows instead of 5-minute windows)
- Count-based SLOs instead of rate-based (alert if more than N errors in a window)
SLOs done well give engineering teams a precise, shared vocabulary for reliability discussions. They make the relationship between toil, incidents, and user experience explicit. They give product managers a principled way to think about risk. And they give on-call engineers something better than gut feel to guide escalation decisions.
The math is straightforward. The tools handle the boilerplate. The hard part is the culture — and the way to build it is to start with a small team, do it right, and let the results speak for themselves.
Comments