DevgainsDevgainsDevgains
All articles

SLI vs SLO vs SLA: Error Budgets Explained

·10 min read

Every team says they want "reliable" software, but few can say what that means as a number. That gap is exactly what the SLI vs SLO vs SLA vocabulary closes. An SLI is what you measure, an SLO is the target you hold yourself to, and an SLA is the promise you make to customers — with money attached. Get these three straight and reliability stops being a vibe and becomes a budget you can spend, track, and defend in a planning meeting. This guide explains each term, shows how error budgets turn an SLO into a decision-making tool, and walks through measuring an SLI with PromQL.

Quick answer

  • SLI (Service Level Indicator): a measured number describing how well a service is doing — e.g. the percentage of successful HTTP requests over the last 30 days.
  • SLO (Service Level Objective): the internal target for that SLI — e.g. "99.9% of requests succeed." It's a goal your team owns.
  • SLA (Service Level Agreement): a contractual promise to customers, usually a weaker number than the SLO, with consequences (refunds, credits) if you miss it.
  • Error budget: 100% − SLO. If your SLO is 99.9%, you're allowed 0.1% failure — that 0.1% is a budget you can spend on risky deploys, experiments, and maintenance.

The order matters: you measure an SLI, target it with an SLO, and promise a looser SLA. SLI → SLO → SLA, tightest to loosest.

Why it matters

Without these definitions, reliability arguments are unwinnable. One engineer thinks the service is "basically fine"; another is paged at 3 a.m. and thinks it's on fire. An SLO replaces the argument with a number everyone agreed to in advance. More importantly, the error budget it implies gives you a shared, quantitative answer to the question that actually blocks teams: should we ship this risky change now, or slow down?

  • Budget remaining? Ship. Take the risk. That's what the budget is for.
  • Budget exhausted? Freeze features, fix reliability, and earn the budget back.

This reframes reliability from "never break anything" (impossible, and it kills velocity) to "break things within an agreed budget." It's the core idea behind Google's Site Reliability Engineering practice, and it's what makes an observability platform worth building — the metrics only matter if they feed a decision.

SLI vs SLO vs SLA at a glance

AspectSLISLOSLA
What it isA measurementAn internal targetAn external contract
AudienceEngineersEngineering + productCustomers / legal
Example99.95% success rate99.9% over 30 days99.5% or credits owed
Consequence of missingNone (it's just data)Error-budget policy kicks inFinancial / contractual
Who sets itObservability toolingThe team that owns the serviceSales + legal + engineering
Typical strictnessThe realityStricter than the SLAThe loosest number

The single most common mistake is collapsing these into one number. Your SLA should always be weaker than your SLO, and your SLO should be achievable given your real SLI history. If you promise customers the same number you barely hit internally, you have no safety margin at all.

Anatomy of a good SLI

A useful SLI is a ratio of good events to total events, expressed as a percentage. The classic categories, drawn from the four golden signals, are:

  • Availability — successful requests ÷ total requests.
  • Latency — requests faster than a threshold ÷ total requests.
  • Quality / correctness — requests served without degradation ÷ total.
  • Freshness — data updated within a window ÷ total (for pipelines and caches).

Notice that latency becomes a count, not an average. "Average latency" hides the tail; a good latency SLI asks "what fraction of requests were faster than 300 ms?" That framing is why picking the right Prometheus metric type — a histogram, not a gauge — matters so much for measuring SLIs correctly.

Measuring an availability SLI with PromQL

Here is an availability SLI computed from a Prometheus counter of HTTP requests, labeled by status code. It returns the fraction of non-5xx responses over a rolling 30-day window:

# Availability SLI: share of successful (non-5xx) requests over 30 days
sum(rate(http_requests_total{status!~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))

Read it inside-out: the denominator is the total request rate; the numerator is the rate of requests whose status code is not a 5xx. Dividing gives the success ratio — your SLI. If this evaluates to 0.9993, you're at 99.93% availability, comfortably above a 99.9% SLO.

For a latency SLI, use a histogram and count requests under your threshold:

# Latency SLI: share of requests served in under 300ms over 30 days
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[30d]))
/
sum(rate(http_request_duration_seconds_count[30d]))

The le="0.3" bucket already contains the cumulative count of requests at or below 300 ms, so dividing by the total count gives the fraction that met the target. Both queries produce a number between 0 and 1 that you compare directly against your SLO. When an SLI dips, a distributed trace is how you find which service ate the budget.

Step-by-step: defining your first SLO

  1. Pick one user-facing journey. Start with the critical path — checkout, login, search — not every endpoint. SLOs are for the flows users actually notice.
  2. Choose an SLI for it (availability and latency are the usual first two).
  3. Look at 4 weeks of history. Your SLO must be reachable. If you've been running at 99.92%, don't set a 99.99% SLO — you'd start every month already over budget.
  4. Set the target slightly below reality, so there's headroom: e.g. observed 99.95% → SLO 99.9%.
  5. Compute the error budget: 100% − 99.9% = 0.1%. Over 30 days that's about 43 minutes of allowed downtime.
  6. Write an error-budget policy — what the team does when the budget runs low (see below).
  7. Alert on burn rate, not just breaches — page when you're spending the budget too fast, not only after it's gone.

Encoding an SLO as configuration

SLOs work best as code so they generate their own alerts and dashboards. Here is an SLO spec in the vendor-neutral OpenSLO-style format (also expressible via tools like Sloth), targeting 99.9% availability with a 30-day rolling window:

apiVersion: openslo/v1
kind: SLO
metadata:
  name: checkout-availability
spec:
  service: checkout
  description: 99.9% of checkout requests succeed over 30 days
  objectives:
    - displayName: Availability
      target: 0.999          # the SLO — implies a 0.1% error budget
  timeWindow:
    - duration: 30d
      isRolling: true
  budgetingMethod: Occurrences
  indicator:
    ratioMetric:
      good:
        query: sum(rate(http_requests_total{status!~"5.."}[5m]))
      total:
        query: sum(rate(http_requests_total[5m]))

The target: 0.999 line is the SLO; everything else describes how to compute the SLI (good ÷ total) and over what window. A generator turns this one file into Prometheus recording rules for the SLI, multi-window burn-rate alerts, and a Grafana dashboard — so the definition and the monitoring never drift apart.

Error budgets in practice

An error budget is the allowed unreliability implied by your SLO. It is the single most useful output of the whole exercise because it converts reliability into a policy:

  • A 99.9% SLO = 0.1% budget ≈ 43 min/month of downtime.
  • A 99.95% SLO = 0.05% budget ≈ 22 min/month.
  • A 99.99% SLO = 0.01% budget ≈ 4.3 min/month.

Each added nine roughly tenths your budget — and each nine costs dramatically more engineering. That trade-off is the point: budgets stop people chasing 100% uptime, which is neither achievable nor worth the cost.

Burn rate tells you how fast you're spending. A burn rate of 1 means you'll exactly exhaust the budget by the end of the window; a burn rate of 10 means you'll be out in a tenth of the time. Alert on fast burn (page now — you'll be out in an hour) and slow burn (a ticket — you're trending over), so a risky deploy that quietly eats budget gets caught before the SLO is blown.

Best practices

  • Fewer SLOs, on real user journeys. Two or three meaningful SLOs beat fifty per-endpoint ones.
  • Make the SLA looser than the SLO. The gap between them is your safety margin.
  • Base targets on history, not aspiration. An SLO you can't hit is just a permanent alert.
  • Alert on burn rate, using multi-window (e.g. 1h + 6h) to balance fast detection and low noise.
  • Review SLOs quarterly. As the service and traffic change, so should the targets.
  • Let the budget drive planning. Budget healthy → ship features. Budget spent → fix reliability.

Common mistakes

  • Setting the SLO to 100%. There's no error budget, so every blip is a violation and every deploy is forbidden. Reliability targets need slack by design.
  • Confusing the SLA and the SLO. Promising customers your internal target leaves zero margin — one bad day breaches the contract.
  • Averaging latency. An average hides the slow tail users feel. Count requests under a threshold instead.
  • Measuring at the load balancer only. If your SLI ignores partial failures and slow-but-200 responses, it will look green while users suffer.
  • No error-budget policy. A budget nobody acts on is just a number on a dashboard. Decide in advance what happens when it runs out.

Key takeaways

  • SLI → SLO → SLA: you measure an SLI, target it with an SLO, and promise a looser SLA.
  • An SLI is a good-events ÷ total-events ratio; availability and latency are the usual first two.
  • An error budget is 100% − SLO — the unreliability you're allowed to spend on risk.
  • Burn-rate alerts catch you spending the budget too fast, before the SLO is blown.
  • Encode SLOs as config so the definition, alerts, and dashboards never drift apart.

FAQ

What is the difference between SLI, SLO, and SLA? An SLI is a measured indicator of service health (e.g. success rate). An SLO is the internal target for that indicator (e.g. 99.9%). An SLA is a contractual promise to customers, usually looser than the SLO, with financial consequences if missed.

What is an error budget? An error budget is the amount of unreliability an SLO permits, calculated as 100% − SLO. A 99.9% SLO gives a 0.1% error budget — roughly 43 minutes of allowed downtime per 30-day month — which the team can "spend" on risky deploys and experiments.

Should the SLA be higher or lower than the SLO? Lower (looser). Your internal SLO should be stricter than the SLA you promise customers, so there's a safety margin between the number you aim for and the number that triggers contractual penalties.

How do I measure an SLI? Compute a ratio of good events to total events over a rolling window. In Prometheus, that's typically sum(rate(good_requests[30d])) / sum(rate(total_requests[30d])), using a histogram bucket for latency-based SLIs.

What is a good SLO target? One you can actually hit based on recent history, set slightly below observed performance. Common targets are 99.9% or 99.95%; each additional nine roughly tenths the error budget and sharply raises engineering cost, so more nines is not automatically better.

What is burn rate? Burn rate is how fast you're consuming the error budget relative to the SLO window. A burn rate of 1 exhausts the budget exactly by the window's end; higher rates mean faster depletion. Multi-window burn-rate alerts are the recommended way to page on SLO risk.

Conclusion

SLIs, SLOs, and SLAs turn "is the service reliable?" into a question with a numeric answer everyone agreed to in advance. The SLI is your measurement, the SLO is your target, the SLA is your promise — and the error budget between "perfect" and your SLO is the currency that lets you ship boldly without pretending nothing ever breaks. Start with one user journey, base the target on real history, wire up burn-rate alerts, and let the budget make the ship-or-slow-down call for you. Reliability stops being an argument and becomes a line item you can manage. Explore more in the observability pillar and the rest of /category/observability.

References: Google SRE Book — Service Level Objectives, Google SRE Workbook — Implementing SLOs, Prometheus querying documentation, OpenSLO specification, and the Sloth SLO generator.

10 min read

Read next