Availability, SLOs and Error Budgets
An SLI is a measurement, an SLO is the target you set for it, an SLA is the contract with penalties; 99.9% availability is 8h 46m of downtime a year and 43 minutes a month, serial dependencies multiply their unavailability, and the error budget — the downtime you are allowed and have not yet spent — is the number that decides whether the next release ships.
"Reliable" is not a requirement; without a measured indicator, a numeric target and a policy for what happens when the target is missed, reliability work is argued about instead of decided, and every outage is a surprise instead of a budget item.
SLI, SLO, SLA — and what "available" means
A Service Level Indicator is a measurement: the fraction of HTTP requests in the last 30 days that returned a non-5xx status within 500 ms. A Service Level Objective is the internal target for that SLI: 99.9%. A Service Level Agreement is a contract with a customer, with a looser number and financial consequences: 99.5%, with service credits below it. The SLO is stricter than the SLA so that you notice and act before the penalty clause does. The three terms are confused constantly; the fix is to remember that only the SLI is a *fact*, and the other two are *decisions* about it.
The properties themselves are distinct. Availability is the fraction of time — or, better, of requests — during which the service does what it should. Durability is the probability that stored data is not lost: S3 promises eleven nines of durability and four nines of availability, which is exactly right — a temporarily unreachable object is fine, a lost one is not. Reliability is the broad term: the service does the right thing, correctly, consistently. A search that returns instantly but wrong is available and unreliable. Define the SLI so that the failure your users actually care about counts — a login that returns 200 in 4 s is not "up" to the person waiting.
The nines, in minutes
Availability is almost always quoted as a percentage, and percentages hide the number that matters: how long you are allowed to be down. Each extra nine divides the allowance by ten. From 99.9% to 99.99% is the step from "one bad afternoon a year" to "52 minutes a year, in total, including every deploy that goes wrong" — and that step typically costs more than all the previous ones combined, because at four nines a human cannot be in the loop for recovery: 52 minutes is roughly one page, one diagnosis, one rollback. Five nines means nobody is ever in the loop; every failure must be detected and mitigated automatically in seconds.
| Availability | Per year | Per month | Per day | What it implies operationally |
|---|---|---|---|---|
| 99% | 3d 15h 36m | 7h 12m | 14m 24s | Business hours on-call; a weekend outage fits |
| 99.5% | 1d 19h 48m | 3h 36m | 7m 12s | Typical SLA for a paid SaaS |
| 99.9% | 8h 45m 36s (≈ 8h 46m) | 43m 12s | 1m 26s | On-call with pager; deploys are the main risk |
| 99.95% | 4h 22m 48s | 21m 36s | 43s | Multi-AZ; automated failover expected |
| 99.99% | 52m 34s | 4m 19s | 8.6s | No human in the recovery loop; automatic rollback and failover |
| 99.999% | 5m 15s | 26s | 0.86s | Multi-region active-active; seconds-scale detection; almost never justified for one service |
How downtime composes
Availabilities are probabilities, and they compose like probabilities. A request that must pass through a load balancer, a service, and a database in series succeeds only if all three do: 0.9999 × 0.999 × 0.999 ≈ 0.998, or 99.8% — worse than the weakest component, which is why a service cannot promise more nines than the dependency it cannot avoid. Ten dependencies at 99.9% each, all in series, give 0.999^10 ≈ 0.990: a 99% system built from 99.9% parts. This is the hidden cost of a synchronous call chain in Microservices and the reason Request/Response vs Event-Driven matters for availability, not just for latency.
Redundancy composes the other way. Two independent instances in parallel, either of which can serve the request, fail only if both do: 1 − (0.01 × 0.01) = 0.9999, so two 99% servers behind a load balancer give 99.99% — *if* their failures are independent, which shared power, a shared deploy, or a shared bad config make untrue. The pattern that actually delivers nines is therefore: remove serial dependencies from the critical path (cache, degrade, queue), and add independent parallel capacity to what remains.
instances in parallel: 1 − (0.001 × 0.001) = 0.999999
critical path in series: 0.9999 × 0.999999 × 0.9995 × 0.999
≈ 0.9984 → 99.84%, ≈ 14 h/yr
weakest serial link: the payment API at 99.9% — no amount of instance redundancy raises the ceiling above itError budgets as a release policy
An SLO of 99.9% over 30 days means you are *allowed* 43 minutes of failure. That allowance is the error budget, and treating it as a budget changes behaviour. While budget remains, the team ships: feature releases, risky migrations, chaos experiments — all are spending from a balance that exists to be spent. When the budget is exhausted, releases stop except for reliability fixes, until the 30-day window rolls enough failure out. The policy replaces the argument between "ship faster" and "be more careful" with a number both sides agreed to in advance.
Alerting follows from the budget too. Alert on burn rate: the multiple of the sustainable rate at which the budget is being consumed. At 99.9%, a burn rate of 14.4 over one hour means 2% of the monthly budget has gone in that hour — page someone. A burn rate of 1 over three days means the budget will be exactly spent by month end — open a ticket. Symptom-based alerting on the SLI ("users are seeing errors") replaces cause-based alerting ("CPU is at 90%"), because a cause without a symptom is not an incident and a symptom without a known cause still is.
- Budget for 99.9% / 30 days: 43m 12s. Two bad deploys at 15 minutes each and one 20-minute database failover and the month is over.
- Multi-window burn-rate alerts (1 h and 6 h, 6 h and 3 d) catch both fast outages and slow degradation without paging on noise.
- The SLO must be achievable with the current architecture; a 99.99% SLO on a single-AZ deployment is a wish, and the budget will simply always be spent.
Measure from the user’s side
A server that reports 100% success while DNS is broken, the CDN is misconfigured, or the load balancer is routing to a dead pool is honestly reporting that every request *it received* succeeded. The SLI that matters is measured as close to the user as possible: synthetic probes from outside your network that exercise the real path (DNS → TLS → LB → app → DB) every minute from several regions; client-side telemetry that reports request outcomes from the browser or mobile app, including the ones that never reached you; and load-balancer logs as the server-side measurement of record, because they see the request before any service does. A request the user sent and you never saw is still a failed request from the SLI’s point of view.
Define the SLI over requests, not time: "99.9% of requests succeed" is more honest than "up 99.9% of the time", because a system that is up but returning errors for one region or one endpoint is partially down, and time-based availability rounds that to zero. The exercise in Logs, Metrics and Traces shows what the histograms behind a latency SLI look like.
Key points
- SLI is the measurement, SLO the internal target, SLA the external contract; make the SLO stricter than the SLA so you act before the penalty.
- 99% = 3d 15h 36m a year; 99.9% = 8h 46m; 99.99% = 52m 34s; 99.999% = 5m 15s. Each nine divides the allowance by ten and roughly multiplies the cost.
- Serial dependencies multiply their availabilities; ten 99.9% dependencies in series are a 99% system. Parallel independent redundancy multiplies the failure probabilities instead.
- The error budget is the unspent downtime; while it lasts you ship, when it is gone you fix. Alert on burn rate, on symptoms, not on causes.
- Measure from the user’s side — probes, client telemetry, LB logs — and over requests, not time.
What 99.9% actually means
| target | per year | per quarter | per month | per week | per day |
|---|---|---|---|---|---|
| 90% | 36d 12h 36m | 9d 3h 9m | 2d 23h 60m | 16h 48m | 2h 24m |
| 99% | 3d 15h 40m | 21h 55m | 7h 12m | 1h 41m | 14m 24s |
| 99.9% | 8h 46m | 2h 11m | 43m 12s | 10m 5s | 1m 26s |
| 99.99% | 52m 36s | 13m 9s | 4m 19s | 1m 0s | 8.6s |
| 99.999% | 5m 16s | 1m 19s | 25.9s | 6.0s | 0.9s |
LB 99.990 × app 99.950 × DB 99.900 = 99.840% → 14h 1m per year, worse than the weakest part
1 − (1 − 0.9900)^2 = 99.9900% → 52m 36s per year — only if failures are independent
SLI service level indicator — the measurement: fraction of requests under 300 ms with a 2xx/3xx status, measured at the edge SLO service level objective — the internal target on an SLI: 99.9% of requests over 30 days SLA service level agreement — the contract with consequences: 99.9% or a 10% credit; always looser than the SLO error budget = 1 − SLO, in minutes (or failed requests) per period; spend it on releases, lose it to incidents
How data moves through it
One request or event, hop by hop.
- 1Client → Synthetic probe / RUM: every user request (and every probe) records outcome and latency at the client or the LB.
- 2LB → Metrics pipeline: access logs streamed to a metrics store; success ratio and latency histograms computed per endpoint.
- 3Metrics → SLO evaluator: SLI over the 30-day window compared with the target; remaining error budget computed in minutes.
- 4SLO evaluator → Alerting: burn-rate rules on short and long windows; page on fast burn, ticket on slow burn.
- 5Error budget → Release pipeline: a policy gate that blocks feature deploys while the budget is negative.
When to use — and when not
- Any service with users who notice when it is down, as soon as there is a second engineer and a deploy cadence to argue about.
- When choosing between architectures: the nines the product actually needs decide whether multi-AZ, multi-region or a single box is the right answer.
- When negotiating an SLA: derive it from a measured SLI and an SLO you already meet, never the reverse.
- Setting an SLO you have no way to measure: an SLO without an SLI is a slogan.
- Applying one availability number to every endpoint; checkout and the admin report deserve different SLOs and different on-call responses.
- Chasing an extra nine that no customer asked for; the cost curve is exponential and the last nine is usually the one that reshapes the architecture.
Tradeoffs
The concepts are cheap; each additional nine is roughly an order of magnitude more infrastructure, automation and on-call, which is why the target must be measured against need.
How it fails
- Server-side 100% while users see failures: the SLI is measured behind the load balancer, so DNS, CDN and LB outages are invisible.
- SLA stricter than what the architecture can deliver: a 99.99% contract on a single-AZ deployment pays credits every quarter.
- Time-based availability hiding partial outages: one region erroring for an hour rounds to "up".
- Alerts on causes not symptoms: paging on CPU at 90% while the real outage is a wrong config that no metric on the box reveals.
- Error budget with no enforcement: the number is tracked, the releases continue, and the SLO becomes a dashboard nobody trusts.
How it scales
- More services in the critical path lower availability multiplicatively; every new synchronous hop must justify its nines. Move consequences off the path with queues; see Request/Response vs Event-Driven.
- From one instance to N behind an LB is the cheapest availability gain (parallel composition); from single-AZ to multi-AZ is the next; multi-region active-active is the last and by far the most expensive.
- SLI pipelines must handle the request volume: compute availability from LB logs or sampled client telemetry, not by querying the production database.
How it interacts with databases, queues, caches, APIs and external systems
- Load balancer and CDN: the server-side source of truth for request outcomes; their logs are the SLI, not the application’s self-report.
- Databases: the durability SLO belongs to backups and replication (RPO/RTO), the availability SLO to failover time; see Replication and Read Scaling.
- Queues: work moved off the synchronous path stops counting against the request SLI and gains its own freshness SLO (oldest message age).
- External providers: their SLA is the ceiling of any path that calls them synchronously; wrap them with the patterns in Reliability Patterns or take them off the path.
- Observability stack: histograms for latency SLIs, structured logs for post-hoc SLI recomputation, traces for finding which hop spent the budget; see Distributed Tracing.