Education › Site Reliability › Stage 1: The reliability mindset

SLIs, SLOs & error budgets

Choose indicators users actually feel, set targets, and spend the budget deliberately.

Intermediate ~35 min read Module 2 of 16

Ask five people whether a service is reliable enough and you will get five opinions, usually shaped by whoever was paged last. Service level objectives replace the opinions with a number that product, engineering and operations agree on in advance. The error budget that falls out of that number is the most useful idea in SRE: it tells you, objectively, when to ship faster and when to slow down. This module teaches you to choose indicators, set targets, and run an error budget policy.

After this module you can
  • Distinguish SLIs, SLOs and SLAs, and explain how they relate
  • Choose SLIs that reflect what users experience, expressed as a ratio of good events to valid events
  • Set a realistic SLO target and window, starting from historical data
  • Calculate an error budget, the budget consumed, and the burn rate
  • Write an error budget policy that changes what the team does when the budget is exhausted

SLI, SLO, SLA

TermWhat it isExample
SLI (indicator)A measurement of some aspect of the service as users experience itThe proportion of checkout requests that succeed in under 500 ms
SLO (objective)A target value for an SLI, over a time window99.9% of checkout requests succeed in under 500 ms, over 28 days
SLA (agreement)A contract with a customer, with consequences if it is missedBelow 99.5% monthly availability, the customer receives service credits

SLIs are what you measure, SLOs are what you aim for, and SLAs are what you promise. An SLA is a business and legal document, and most internal services have none. Where one exists, set the internal SLO stricter than the SLA, so that you get a warning, and time to react, well before you owe anybody money.

The subtle point is that an SLO is both a floor and a ceiling. Falling below it means users are unhappy and reliability work takes priority. But greatly exceeding it also carries a cost: you could have shipped faster or spent less, and users come to depend on a level of service you never promised. Google has deliberately taken down an over-reliable internal service for short periods so that its consumers could not quietly build a dependency on it never failing.

Choosing good SLIs

A good SLI rises and falls with user happiness. CPU utilisation is not an SLI: users do not care about it, and it can be at ninety percent while everyone is perfectly happy. "Did my page load, quickly and correctly?" is what they care about. The Google SRE Workbook recommends expressing every SLI the same way, as a ratio:

text
SLI = good events / valid events x 100%

valid events : everything that should count   (e.g. all HTTP requests except health checks)
good events  : those that met the criterion   (e.g. status below 500 AND served in < 500 ms)

That form has real advantages. It always runs from 0 to 100%, where 100% is perfect. It is easy to reason about, the error budget drops straight out of it, and the same alerting logic works for every SLI you define. Which indicators to use depends on the kind of system.

System typeUseful SLIs
Request-driven (APIs, websites)Availability: proportion of requests that succeed. Latency: proportion faster than a threshold. Quality: proportion served without degradation.
Data pipelines, batchFreshness: proportion of data updated more recently than a threshold. Correctness: proportion of records producing the right output. Coverage: proportion of data processed.
StorageDurability: proportion of written data that can be read back intact.
  • Measure as close to the user as you can. Server-side metrics are easy but miss failures in the load balancer, DNS or the network. Load balancer logs see more; synthetic probes and real-user monitoring see the most. Start with what you have and move outward.
  • Define latency as a proportion under a threshold, not as an average. Averages hide the slow tail, and "99% of requests under 500 ms" fits the good-over-valid form. Use two thresholds if needed, for example 90% under 200 ms and 99% under 1 s.
  • Decide what is valid. Exclude health checks and traffic from your own load tests. Think carefully about 4xx responses: a 404 for a page that does not exist is the service working correctly, while a wave of 401 errors caused by a broken auth deploy is not.
  • Keep the number small. Two to four SLIs per user journey, for the journeys that matter most: log in, search, check out. Not one for every endpoint.
Availability and latency SLIs for a checkout service, over 28 days
promql
# availability: proportion of requests that did not fail server-side
sum(rate(http_requests_total{job="checkout", code!~"5.."}[28d]))
/
sum(rate(http_requests_total{job="checkout"}[28d]))

# latency: proportion of requests served in under 500 ms (histogram bucket le="0.5")
sum(rate(http_request_duration_seconds_bucket{job="checkout", le="0.5"}[28d]))
/
sum(rate(http_request_duration_seconds_count{job="checkout"}[28d]))

Setting the target

Do not start from what sounds impressive. Start from two questions: what do users need, and what does the system do today?

  1. Measure current performance over the last month or quarter. If the service has delivered 99.7%, an SLO of 99.99% is fiction that will be in breach from its first day and ignored by the second week.
  2. Set the first target at or slightly below what you already achieve, as long as users are reasonably happy at that level. An achievable SLO that people act on is worth more than an ambitious one nobody believes.
  3. Check it against your dependencies. A service cannot be more available than the critical things it depends on, unless you add redundancy, caching or graceful degradation. If every request needs a database with a 99.95% objective, promising 99.99% is not credible.
  4. Choose a window. A rolling window of 28 or 30 days is the common choice. Four weeks always contains the same number of weekends, which makes periods comparable. Calendar months suit reporting to the business but reset abruptly.
  5. Agree it with the product owner, in writing. The SLO is a product decision about how reliable the feature needs to be. Engineering cannot set it alone.
  6. Review it every quarter or so. Tighten it if users complain while you are meeting it, and loosen it if you keep missing it while nobody notices.
Tip

A useful test of whether an SLO is right: when you are just meeting it, are users broadly happy? When you are missing it, do they complain? If both answers are yes, the SLO is measuring the right thing at the right level.

An SLO recorded as code, in the OpenSLO style
yaml
apiVersion: openslo/v1
kind: SLO
metadata:
  name: checkout-availability
  displayName: Checkout requests succeed
spec:
  service: checkout
  description: Proportion of checkout API requests that do not fail with a 5xx
  budgetingMethod: Occurrences
  timeWindow:
    - duration: 28d
      isRolling: true
  objectives:
    - displayName: Successful requests
      target: 0.999

Error budgets

If the target is 99.9%, then 0.1% of events are allowed to fail. That allowance is the error budget. It is not a number to minimise. It is a resource to spend, on releases, migrations, experiments, chaos tests and planned maintenance, all of which carry some risk of failure.

ratiocompared withleavesburn rateremainingEventsvalid vs goodSLIgood / validSLO99.9% over 28 dError budget1 - SLO, spent so farBurn-rate alertspage or ticketBudget policyship, or fix first
From measurement to decision: raw events become an SLI, the SLI is compared with the SLO to give an error budget, and the burn rate of that budget drives both the alerts and the release policy.
text
error budget (fraction)  = 1 - SLO                         99.9%  ->  0.1%
error budget (events)    = (1 - SLO) x valid events        10,000,000 requests  ->  10,000 may fail
error budget (time)      = (1 - SLO) x window              28 days  ->  40.3 minutes of full outage

budget consumed          = bad events so far / error budget in events
budget remaining         = 1 - budget consumed

Work an example. A service receives 10 million requests in 28 days with a 99.9% SLO, so the budget is 10,000 failed requests. A bad deploy causes 2,500 errors, a dependency outage another 1,500. That is 4,000 bad events, so 40% of the budget is consumed and 60% remains. An incident in which 20% of requests fail for two hours costs far less than a total outage of the same length, and a request-based budget accounts for that correctly, which is why it is better than counting minutes of downtime.

Burn rate tells you how fast the budget is being spent, relative to the rate that would use it up exactly at the end of the window.

text
burn rate = observed error rate / (1 - SLO)

SLO 99.9%  ->  allowed error rate 0.1%
  error rate 0.1%   ->  burn rate 1     budget lasts exactly the window (28 days)
  error rate 0.2%   ->  burn rate 2     budget gone in 14 days
  error rate 1.44%  ->  burn rate 14.4  budget gone in under 2 days
  error rate 100%   ->  burn rate 1000  budget gone in about 40 minutes

time to exhaustion = window / burn rate
python
def error_budget_report(slo: float, valid: int, bad: int, window_days: int = 28) -> dict:
    """slo as a fraction, e.g. 0.999. valid/bad are event counts so far in the window."""
    budget_events = (1 - slo) * valid
    consumed = bad / budget_events
    return {
        "sli": 1 - bad / valid,
        "budget_events": round(budget_events),
        "budget_consumed": round(consumed, 3),
        "budget_remaining": round(1 - consumed, 3),
        "full_outage_minutes_allowed": round((1 - slo) * window_days * 24 * 60, 1),
    }


print(error_budget_report(slo=0.999, valid=10_000_000, bad=4_000))
# sli 0.9996, budget 10000 events, 0.4 consumed, 0.6 remaining, 40.3 minutes

Burn rate is the basis of good alerting, which the alerting module covers: a high burn rate for a short time deserves a page, and a modest burn rate sustained for days deserves a ticket.

The error budget policy

An SLO that changes nobody's behaviour is just a dashboard. The error budget policy is a short document, agreed in advance by engineering, product and management, that says what happens as the budget is spent. Agreeing it before the crisis is the point: during an incident nobody negotiates well.

A minimal error budget policy
text
Service: checkout          SLO: 99.9% availability, rolling 28 days
Owners: checkout team (engineering), J. Rivera (product)

Budget remaining > 50%
  Normal operation. Releases proceed at will. Risky work (migrations,
  chaos experiments) is encouraged while there is budget to absorb it.

Budget remaining 0-50%
  Releases continue. Reliability items from recent postmortems move to
  the top of the next sprint. Risky work needs the on-call's agreement.

Budget exhausted (SLO missed over the window)
  Feature releases stop. Only reliability fixes, security patches and
  changes required by law ship until the service is back within SLO.
  The team spends its time on the causes identified in postmortems.

A single incident consuming > 20% of the budget
  A postmortem is mandatory, with at least one priority action item.

Exceptions and disputes
  Escalate to the engineering director, who decides.
  Outages caused by another team's service are attributed to that service.

Notice what the policy does to incentives. Developers want to ship, and now the way to keep shipping is to keep the service reliable, so they start asking for canaries and better tests themselves. Operators who wanted to block risky changes have no grounds to do so while budget remains. The argument about whether it is safe to release has been replaced by a number both sides accepted beforehand.

Watch out

A release freeze needs management backing that was secured in advance. If the first time the budget runs out someone senior overrides the policy for a deadline, everyone learns that the SLO is decoration. Better a looser SLO that is enforced than a strict one that is waived.

Hands-on practice

Write your first SLO and its budget

  1. Choose one user journey on a service you know, such as "log in" or "load the dashboard". Describe in one sentence what success looks like to the user.
  2. Define an availability SLI and a latency SLI for it in good-over-valid form. State explicitly which events are valid and which are good, including how you treat 4xx responses and health checks.
  3. From your metrics, logs or load balancer data, measure both SLIs over the last 28 days. If you have no data, state where you would measure, and why that point is close enough to the user.
  4. Propose targets based on what you measured, and check them against the availability of the service's critical dependencies.
  5. Calculate the error budget in events and in minutes of full outage. Then take a recent incident and work out how much of the budget it consumed.
  6. Write a half-page error budget policy with three thresholds and a named decision-maker for disputes.
  7. Present the SLO and the policy to a product owner or a colleague acting as one, and record what they pushed back on.
Cheat sheet

SLIs, SLOs & error budgets — at a glance

Main things to focus on

  • SLI is what you measure, SLO is what you aim for, SLA is what you promise. Keep the SLO stricter than any SLA.
  • SLI = good events / valid events. Measure what users experience, as close to them as you can.
  • Latency SLIs are proportions under a threshold, never averages.
  • Start targets from measured history and users' needs, not from what sounds impressive.
  • A service cannot beat the availability of its hard dependencies without redundancy or degradation.
  • Error budget = 1 - SLO. It is a resource to spend on change, not a number to minimise.
  • Burn rate = error rate / (1 - SLO). Burn rate 1 spends the budget exactly over the window.
  • An SLO without an agreed, enforced error budget policy changes nothing.

Formulas

SLI = good events / valid eventsAlways 0-100%, higher is better
error budget = 1 - SLO99.9% gives 0.1%
budget in events = (1 - SLO) x valid events10M requests at 99.9% gives 10,000
budget in time = (1 - SLO) x window99.9% over 28 days gives 40.3 minutes
budget consumed = bad events / budget in events4,000 / 10,000 = 40%
burn rate = error rate / (1 - SLO)1% errors at 99.9% is a burn rate of 10
time to exhaustion = window / burn rate28 days at burn rate 14 is 2 days

Budget in minutes of full outage

99% over 28 d403 minutes (6.7 hours)
99.5% over 28 d201.6 minutes
99.9% over 28 d40.3 minutes
99.95% over 28 d20.2 minutes
99.99% over 28 d4.03 minutes

SLI menu

AvailabilityProportion of valid requests served successfully
LatencyProportion of valid requests served faster than a threshold
QualityProportion served without degraded content
FreshnessProportion of data updated more recently than a threshold
CorrectnessProportion of records that produced correct output
CoverageProportion of input data that was processed
DurabilityProportion of stored data that can be read back

PromQL patterns

sum(rate(http_requests_total{code!~"5.."}[28d])) / sum(rate(http_requests_total[28d]))Availability SLI
sum(rate(..._bucket{le="0.5"}[28d])) / sum(rate(..._count[28d]))Latency SLI: proportion under 500 ms
1 - SLIObserved error ratio
(1 - SLI) / (1 - 0.999)Budget consumed over the same window, as a fraction

Error budget policy skeleton

scopeService, SLO, window, owners in engineering and product
budget healthyShip freely; risky work encouraged
budget lowReliability items prioritised; more caution
budget exhaustedFeature freeze; only reliability and security changes
large single incidentMandatory postmortem with priority actions
escalationA named person who settles disputes

Common pitfalls

  • Using CPU, memory or uptime of a server as an SLI, none of which users experience.
  • Setting 99.99% because it sounds professional, when the service has never achieved it.
  • Measuring latency as an average, which hides the slow requests that make users leave.
  • Defining dozens of SLOs, so that none of them gets attention.
  • Counting only minutes of total outage, which ignores partial failures affecting some users.
  • Writing an error budget policy that leadership overrides the first time it is triggered.
Quiz

Check your understanding

5 questions · 4 to pass · answers are explained as you go. Your best score is saved on this device only.

Progress and quiz scores are saved in this browser only. Back up or restore on the hub.

Was this lesson useful? Tell me what to improve →