> ## Documentation Index
> Fetch the complete documentation index at: https://docs.use.observer/llms.txt
> Use this file to discover all available pages before exploring further.

# SLOs and error budgets

> How service level objectives translate metric status into a contractual signal.

> [!NOTE]
>
> **In short**
>
> * An **SLO** promises a metric is up (healthy or degraded) for a share of a rolling window, for example 99.9% over 30 days.
> * The **error budget** is what's left of the allowed downtime. 99.9% over 30 days allows 43.2 minutes.
> * **Unhealthy** readings burn budget. A degraded reading counts as up.
> * **No data and silence** follow the SLO's unknown-data policy. The default, `down`, counts them as downtime.
> * A **burn event** opens (and alerts) only when the budget goes below zero.

An SLO turns "is this up right now?" into a running balance: the
**error budget**, the remaining allowance of unhealthy readings.

## Definition

An SLO has three core fields:

* **Metric**: which metric the SLO observes.
* **Target percentage**: the fraction of the window the metric
  must be `healthy` or `degraded`. Common values: 99, 99.5, 99.9, 99.95, 99.99.
* **Window in days**: the rolling period the target applies to.
  Common values: 7, 30, 90.

A fourth field, the **unknown-data policy**, controls how samples
without a verdict are counted. It is optional and defaults to the
strictest reading; see
[Unknown-data policy](#unknown-data-policy) below.

A fifth field, the **maintenance policy**, controls whether planned
maintenance spends error budget. It defaults to pausing the budget;
see [Planned maintenance](#planned-maintenance) below.

The window is rolling: at any instant, the SLO looks back N days
and computes the fraction of samples in that window that resolved
to healthy or degraded.
There is no calendar boundary that resets the budget.

## Window start

The rolling window never reaches back before the SLO was created.
A new SLO on a metric with months of prior history measures only
from its own creation time: the older samples remain visible on
the metric, but they neither earn nor burn budget. Until the SLO
has been observing for a full window length, the calculation
covers the period since creation; after that, the window rolls
normally.

Because no state survives deletion, deleting an SLO and recreating
it starts a clean epoch. This is the supported way to reset an SLO
after a definitional change (for example, a corrected threshold
that invalidates the old verdicts).

For the first 24 hours of observation the budget reads
**collecting** instead of a number. A percentage computed from a
few hours of samples swings too widely to act on.

After that, the budget reads **No data** while the metric is stale,
and returns on the first evaluation after fresh samples arrive. The
silent gap then counts by the
[unknown-data policy](#unknown-data-policy): under `down`, as
downtime. See
[Thresholds and confirmation time](https://docs.use.observer/docs/concepts/thresholds-and-dwell.md) for how
staleness is decided.

## Error budget

Given a 99.9% target over 30 days, the budget allowance is:

```text theme={null}
allowance = 30 days * (1 - 99.9 / 100)
         = 30 days * 0.001
         = 43.2 minutes per 30-day window
```

The budget burns on `unhealthy` samples. A sample counts as good
when it resolves to `healthy` or `degraded`: a degraded metric is
slower but still serving, so it counts as up, the same rule SLA
attainment uses by default (the
[threshold operators reference](https://docs.use.observer/docs/reference/threshold-operators.md)
covers each status). "Resolves to" matters for `no_data`: the SLO
reads the same folded status the status page paints, so a `no_data`
sample on a metric whose no-data treatment is `healthy` or
`degraded` counts as good. Other `no_data` samples follow the
unknown-data policy below. The SLO page and SLA statements count the
same samples the same way.

The unknown-data policy, described next, decides how samples without
a verdict (`no_data`) and silent gaps are counted.

The reported percentage is clamped to the range -9999.9999 to
9999.9999. An SLO that has burned many multiples of its allowance
reads as a large negative number rather than an unbounded one.

## Unknown-data policy

Every SLO carries an unknown-data policy that answers one question:
what does a sample without a verdict mean? The policy decides how
samples without a verdict count. In SLA attainment, time in hours
with no status sample at all is also treated as unknown; see
[Monitoring coverage](#monitoring-coverage).

**Silence** is a gap between samples longer than the check's
reporting cadence allows (the [staleness cutoff](https://docs.use.observer/docs/concepts/thresholds-and-dwell.md)),
judged by the cadence in force when the gap started. It counts only:

* after the metric's first sample,
* outside plan-suspended time,
* for agents on version 1.4.0 or later, and for checks Observer runs
  itself (hosted checks, heartbeats, inbound alerts). Older agents
  report only on change, so their gaps are normal and never count.

A silent gap becomes readings at the rate the metric normally reports.

| Policy | Unknown counts as | Notes |
| - | - | - |
| `down` | Against the budget and the SLA | The strictest reading. **Default.** |
| `exclude` | Removed from the calculation | Monitoring coverage is disclosed alongside the number, so the smaller denominator is visible. Matches the common vendor-SLA reading that downtime is a failed check, not an absent check. |
| `up` | Up | Only for contracts that specify it. Not recommended. |

The SLO's policy governs the live error budget and the SLO page.
SLA Ledger contracts carry their own missing-data rule (default:
excluded with coverage disclosed); see
[SLA methodology](https://docs.use.observer/docs/concepts/sla-methodology.md).

In config-as-code, the `slos` entry accepts an optional field:

```yaml theme={null}
unknown_policy: down | exclude | up
```

Omitting the field means `down`. Exports include the field only
when it is set to a non-default value.

## Planned maintenance

Planned maintenance is usually agreed with the people who depend on
the service, so by default it does not spend error budget. Every SLO
carries a maintenance policy:

| Policy | Samples inside maintenance | Notes |
| - | - | - |
| `exclude` | Removed from the budget calculation | Pauses the error budget. **Default.** |
| `count` | Counted like any other sample | Maintenance downtime burns budget and can open a burn event. |

A sample is inside maintenance when it falls in the scheduled window
of a published maintenance that lists the SLO's service. Drafts
pause nothing. A maintenance that is cancelled stops pausing the
budget at the moment it is cancelled, so one cancelled before it
starts pauses nothing. Time that overruns the scheduled end counts
normally.

Under `exclude`, paused samples leave both sides of the calculation:
they neither count as good nor burn budget, and downtime inside the
window cannot open a burn event or send `slo.burn_started`. If every
sample in the window falls inside maintenance, the budget shows no
number (**No data**).

The policy covers the live error budget and burn alerts. SLA
attainment has its own maintenance rule, set per report or contract
line; see [SLA methodology](https://docs.use.observer/docs/concepts/sla-methodology.md).

Set the policy on the SLO's create or edit form, under **Planned
maintenance**. The public API returns it as `maintenance_policy` on
every SLO. In config-as-code, the `slos` entry accepts an optional
field:

```yaml theme={null}
maintenance_policy: exclude | count
```

Omitting the field means `exclude`. Exports include the field only
when it is set to `count`.

## Monitoring coverage

The SLO page, SLA Ledger statements and
[evidence packs](https://docs.use.observer/docs/guides/evidence-packs.md) show **monitoring
coverage**: the share of measured time in which the check was
reporting. Measured time is the window after the SLO was created,
outside maintenance and exclusion windows. It is split into UTC hours,
and an hour counts as observed when it holds at least one sample with
a status (healthy, degraded or unhealthy).

* Coverage is time based, so it does not depend on the push interval,
  and changing the interval does not change the coverage of past
  windows. A gap shorter than an hour, inside an hour that has
  samples, does not lower it.
* An hour cut by the window's start or end, or by a maintenance or
  exclusion window, counts as observed if the check reported a status
  anywhere in that hour.
* The hour in progress is left out until its first sample arrives.
* Coverage is a disclosure. What counts against the budget is
  silence, as described under
  [unknown-data policy](#unknown-data-policy).
* History is read from individual samples (kept 90 days), then from
  15 minute status counts (kept about 400 days), so older periods stay
  measurable.

On the SLO page, **Monitoring coverage** shows the percentage and the
time without samples over the window. **How gaps count** shows the
unknown-data policy, the silent time counted as down, how many
readings counted as unknown, and how many were excluded. Below 99%
the coverage is marked partial; below 90% the page warns that
attainment may be overstated until the gaps are fixed.

Low coverage signals monitoring gaps rather than service downtime. Under the `exclude` policy, coverage is the
disclosure that keeps the availability number honest: a 100%
availability figure computed over 60% coverage reads very
differently than the same figure over full coverage.

## Burn events

A burn event opens when the error budget goes **negative**, meaning the
metric has recorded more unhealthy samples than the objective
allowed.
It is not triggered by an individual `unhealthy` sample, and not by
the budget merely dropping below 100% remaining: a budget at 40%
remaining is being consumed as designed and opens nothing. The
event closes on the first evaluation where the budget is back above
zero. Each burn event records its start, end, and the percent of
the budget it consumed.

Two grace conditions gate the opening, so a young or thinly-sampled
SLO cannot page on noise:

* The SLO must be at least one hour old.
* The window must hold at least 10 samples.

Until both hold, a negative budget is still reported on the SLO
page, but no burn event opens and no `slo.burn_started` fires.

Under the default maintenance policy, downtime inside planned
maintenance is not part of the budget, so it cannot open a burn
event on its own.

Webhook subscribers receive `slo.burn_started` when an event opens
and `slo.burn_resolved` when it closes. Pair the two by their
`burn_event_id`. If alerts were off when the burn started and are
turned back on while it continues, a fresh `slo.burn_started` goes out
for the same burn, with the original start time.

## Recovery projection

When the budget is negative, the SLO page shows the projected date
the budget returns to positive. The projection assumes no further
breaches: as the window rolls forward, old unhealthy samples age
out and the good fraction recovers on its own. The date answers
the operational question "when does this SLO stop being red if
nothing else goes wrong?"

## Exclusion windows

An operator can exclude a specific period from SLA attainment.
Typical cases: a misconfigured threshold that produced false
unhealthy verdicts, or a telemetry outage that the unknown-data
policy would otherwise count against the service.

Exclusion windows apply to SLA attainment only: the SLA panel,
customer SLA reports and SLA Ledger statements. The live error
budget and burn events ignore them. The budget counts every sample
in the window except planned maintenance, and only when the SLO's
[maintenance policy](#planned-maintenance) is `exclude` (the
default, which pauses the budget).

Exclusions are deliberately constrained:

* A written reason is required for every window.
* Each window is capped at 30 days.
* Creating or deleting a window is recorded in the audit log.
* Every exclusion is listed, with its reason, in evidence packs.

The constraints exist for one property: numbers never improve
silently. An excluded period changes the result, and the change is
visible to anyone reading the evidence.

## Picking a target

The right SLO target reflects the system's actual achieved
availability over the prior 90 days, plus a margin for the
behaviour you want to drive. Three common starting points:

* **99.5%** for a new service or unknown baseline. Loose enough
  that noise does not drive false alerts.
* **99.9%** for a service with a stable history and a reasonable
  remediation pipeline.
* **99.99%** for systems where customers feel every minute of
  unhealthy time. Requires investment in error-handling and rapid
  remediation; otherwise the target produces churn rather than
  signal.

> [!WARNING]
>
> **Tighter is not always better**
>
> A target tighter than the system's achieved availability burns
> budget on noise and trains the on-call team to ignore alerts.
> Start at the 90-day baseline and only tighten as the underlying
> system improves.

## Per-customer targets

Different customers can sign different SLO targets against the same
underlying metric. The model and configuration steps live in
[Customer scopes](https://docs.use.observer/docs/concepts/customer-scopes.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.