Service Level Objectives (SLOs)
A practical guide to monitoring what matters to your users — without drowning in alerts.
What is an SLO?
A Service Level Objective is a target you set for how reliable a service should be, measured over a window of time.
Example: "My API should be available 99.9% of the time, measured over the last 30 days."
That single sentence contains three numbers that drive everything else:
- What you measure ("availability")
- How well it must perform ("99.9%")
- Over how long ("rolling 30 days")
SLOs convert vague reliability goals like "keep it up" into concrete numbers. They tell you when to relax ("we're at 99.95% — under target, all good") and when to focus on stability instead of features ("we've burned the entire monthly budget — feature freeze").
If you've never set an SLO before, skip ahead to Getting started — the rest of this page is reference material you can return to.
Three letters that get confused
| Term | What it is | Example |
|---|---|---|
| SLI — Service Level Indicator | The raw metric you measure | "% of HTTP checks returning 2xx within 30 seconds" |
| SLO — Service Level Objective | The internal target you set on an SLI | "availability ≥ 99.9% over rolling 30 days" |
| SLA — Service Level Agreement | A contractual promise to a customer with financial penalty if missed | "99.5% monthly uptime, otherwise 10% account credit" |
The relationship: SLI is what you observe → SLO is what you commit to internally → SLA is what you promise customers.
In practice the SLO target is stricter than the SLA target. If your customer SLA is 99.5% monthly, your internal SLO might be 99.9% rolling 30-day. The gap is your buffer — you have time to fix issues before missing the contractual SLA.
How Enori SLOs work
Each SLO you create on a monitor tracks one signal continuously. Enori does three things in the background:
- Measures the chosen signal (uptime, latency, or content match) on every check
- Computes how much "error budget" you have left in the current window
- Alerts you if you're burning the budget faster than you can afford
You can create multiple SLOs per monitor. Common setup for a production API:
| Purpose | SLI | Target | Window |
|---|---|---|---|
| Engineering health | Availability | 99.9% | rolling 30 days |
| User experience | Latency (<500ms) | 95% | rolling 28 days |
| Customer SLA | Availability | 99.5% | calendar monthly |
The first two drive day-to-day decisions. The third generates customer-facing reports.
The three SLI types
Enori supports three indicators today. Pick the one that matches what your users actually feel.
Availability
% of checks that succeeded.
A check succeeds by its own monitor type's rule. For a Website or API monitor that means a 2xx status code within the timeout, passing any keyword/SSL/content rules you've configured; for a Ping, Port or DNS monitor it means that check's own success condition. Availability SLOs work on every monitor type.
Use it for: general health monitoring. "Is my service up?" This is the default and the right starting point for most teams.
Latency
% of checks completed under a threshold (e.g. 500ms).
You set the threshold in milliseconds. A check is "good" if its response time is at or below that threshold; otherwise it's "bad" — even if the request succeeded. That is the same whole-request figure the response-time chart plots (connect + TLS + first byte + body), not the separate time-to-first-byte metric.
Use it for: UX-sensitive services where a slow response is as bad as a failure. Checkout pages, search APIs, dashboards.
Not available on Domain or Job monitors. Their checks record no response time — a Domain monitor reads stored registry data and a Job monitor receives a heartbeat rather than calling out — so a latency SLO there would read 100% forever. Enori refuses to create one and tells you to use an availability SLO instead.
Content match
% of checks where the configured keyword/regex matched in the response body.
You configure the keyword on the monitor itself. The SLO tracks how often that keyword shows up.
Use it for: verifying business logic stays correct. "Does the homepage still show 'Welcome'?" / "Is the API returning the expected JSON shape?"
The keyword and the SLO are linked in both directions. You can't create a content-match SLO on a monitor that has no expected keyword, and you can't clear the keyword afterwards while such an SLO exists — see When Enori refuses a change below.
Window types: rolling vs calendar
The window is the period over which the SLO is computed. Two flavors:
Rolling window
"The last N days, always."
A rolling window slides every minute. "Rolling 30 days" always means "now − 30 days → now." Old incidents drop off the back as time moves forward.
Why use it: continuously-current view of reliability. Engineers prefer this because it shows the actual recent state of the service. Default for most teams.
Available lengths: 7, 28, 30, 90, or 365 days.
Calendar window
"The current month / quarter / year."
A calendar window is anchored to clock boundaries. "Calendar monthly" runs from the 1st of the month at 00:00 UTC to the 1st of the next month. The window resets cleanly on each boundary.
Why use it: SLA contracts. Customer agreements typically read "99.5% per calendar month", not "99.5% rolling 30 days". Calendar windows make SLA reports straightforward — there's a clean before/after on each boundary.
Available cycles: weekly (Monday-aligned), monthly, quarterly, yearly.
Tip: create both. A rolling SLO for engineering insight + a calendar SLO matching your customer agreement is a common combo.
Reading the SLO card
When an SLO is active, you'll see this on the monitor's detail page:
─────────────────────────────────
99.93% achieved · 99.9% target
████░░░░░░░ 42% burned
25m budget left
0.6× burn rate · Healthy
─────────────────────────────────(A sketch of the layout, not a pixel-accurate one — but the status word at the bottom is the real label: Healthy, At risk, Exhausted or Disabled.)
What each number means:
| Field | Meaning |
|---|---|
| Achieved | Actual measured % over the window. Higher is better. |
| Target | The level you committed to. |
| Burned % | How much of the error budget is spent. |
| Budget left | How much "bad time" you can still afford before missing the SLO. |
| Burn rate | Multiplier vs sustainable. 1.0× = exactly on pace. 0.6× = under pace (good). 5.8× = burning way too fast. |
Status colors
- 🟢 Healthy — under 60% of the budget burned.
- 🟡 At risk — 60% to under 100%. Watch closely.
- 🔴 Exhausted — 100% or more. Budget gone.
- ⚪ Disabled — the SLO is switched off and is not measuring anything. The rail replaces its budget line with "Not measuring", and it sorts to the bottom of the list, below even the healthy SLOs. It is deliberately outside the colour ladder: a switched-off SLO is neither healthy nor at risk, it simply has no verdict.
There's one more state that isn't a colour: an SLO too young to be scored yet reads "Not enough data" instead of a percentage. A latency SLO at 99.9%, for instance, needs 500 checks before a single slow one is even affordable, so until then there is no budget to burn and nothing honest to report. The public status page shows the same state as "not enough data yet".
Error budgets and burn rate
The error budget is the amount of "bad time" your SLO permits over the window.
If you target 99.9% availability over 30 days:
- Window = 30 days × 86,400 seconds = 2,592,000 seconds
- Allowed bad time = 2,592,000 × (1 − 0.999) = 2,592 seconds (≈ 43 minutes)
That's your budget. Every minute of downtime spends from it.
Burn rate is how fast you're spending the budget compared to the sustainable rate. The sustainable rate is the pace that would exhaust the budget exactly at the window's end — perfectly even consumption.
| Burn rate | What it means |
|---|---|
0.0× – 0.5× | Way under pace. You have headroom. |
0.5× – 1.0× | Slightly under pace. Healthy. |
1.0× | Exactly on the sustainable line. |
1.0× – 6× | Over pace. Nothing pages yet, but the budget is shrinking faster than the window replaces it. |
≥ 6× (sustained 6 h) | Slow-burn alert fires. |
≥ 14.4× (sustained 1 h) | Fast-burn alert fires. Major incident in progress. |
The thresholds (14.4× and 6×) come from Google SRE practice, paired with the evaluation window each is measured over — 14.4× over 1 hour, 6× over 6 hours. Those hours are how long the rate must hold before Enori pages you, not how long the budget would survive: at a sustained 14.4×, a 30-day budget lasts about 50 hours, and 1 hour at that rate spends roughly 2% of it.
Burn-rate alerts
When you create an SLO, four alert types are available. They route through the same alert channels you've configured on the monitor — email, Slack, Discord, Teams, webhook, push notifications. No extra setup.
| Alert | Toggle reads | Fires when | Checked |
|---|---|---|---|
| Fast burn | Fast burn (1h, 14.4×) | Burn rate is at or above 14.4× sustainable | every minute |
| Slow burn | Slow burn (6h, 6×) | Burn rate is at or above 6× sustainable | every 5 minutes |
| Low budget | Low budget (under 25% remaining) | More than 75% of the budget is burned — i.e. under 25% left. Exactly 25% left does not fire | every 10 minutes |
| Budget exhausted | (no toggle — see below) | The whole error budget is gone (100% burned) | every 10 minutes |
Both burn-rate alerts use two windows, not one. Fast burn needs the rate at 14.4× or above across the whole hour and across the last 5 minutes; slow burn needs 6× across 6 hours and across the last 30 minutes. The short window is what makes the alert clear once you've fixed the problem, instead of staying open for the rest of the long window.
The first three have their own toggle. Three presets set them together:
| Preset | Fast burn | Slow burn | Low budget |
|---|---|---|---|
| Aggressive — all three triggers fire | ✓ | ✓ | ✓ |
| Balanced — fast + slow burn (the default) | ✓ | ✓ | — |
| Relaxed — slow burn only | — | ✓ | — |
Pick one, or set the three toggles individually. Note that Low budget is off unless you choose Aggressive or switch it on yourself.
Budget exhausted has no toggle of its own: it is the terminal event of the feature, so it is armed whenever any of the other three is on, and silenced only when you turn all three off.
When an alert fires, it opens an alert episode — a stateful incident that you can acknowledge, snooze, or resolve directly from the email, push notification, or Slack message. Episodes auto-close when conditions return to normal for 30 continuous minutes.
The "checked every…" column above is how often Enori evaluates the condition, not how often it messages you. Every SLO burn alert on a monitor merges into one episode, so a condition that stays true across many checks notifies you once and then records the repeat fires quietly on the episode. The one exception is escalation in severity: a Low budget alert opens at info, so if the budget then runs out entirely, the warning-level exhausted alert does reach you rather than being swallowed as a duplicate.
Maintenance and pause exclusion
By default, two kinds of "downtime" are excluded from the SLO:
| Exclusion | What it excludes |
|---|---|
| Maintenance windows | Checks that ran inside a maintenance window declared on the monitor |
| Paused time | Periods when the monitor was paused (manually or by plan downgrade) |
Both are on by default and recommended for almost every SLO. Planned downtime shouldn't burn customer-trust budget, and pausing a monitor shouldn't trick the SLO into thinking everything was healthy.
You can toggle either off when creating the SLO if you want strict counting (rare).
How the "maintenance excluded" figure is measured
All the SLO numbers — achieved %, budget burned, and the "maintenance excluded" seconds — treat one-time and recurring (daily / weekly / monthly) windows identically. Every check result is tagged "in maintenance" at the moment it runs, and both the calculations and the excluded-seconds figure are derived from those tags.
That also defines what the figure means: it counts the monitoring time that was actually covered by maintenance (checks in maintenance × your check interval), not the scheduled length of the window. A 4-hour window on a monitor checked every 5 minutes shows ≈4 hours; a window scheduled over a monitor that ran no checks contributes nothing. One deliberate approximation: while a monitor is down it is re-checked every 30 seconds regardless of its configured interval, so a window in which the monitor was down can show more excluded time than its wall-clock length — the same convention your uptime% has always used, so the two figures agree.
Both the SLA report's maintenance list and the recent-breaches view itemise every window type by name — recurring windows appear once per occurrence, recorded as each occurrence actually runs (from 2026-07-29 onward; occurrences before that date were never recorded and cannot be listed).
SLA reports for calendar SLOs
If you have a customer SLA that reads "99.5% per calendar month", create a calendar-monthly SLO with the matching target. Then on the SLO management page (/dashboard/monitors/{id}/slos) the Generate SLA report button activates.
The button is enabled only when the monitor has a calendar-monthly SLO; without one it stays greyed out and reads "SLA reports require a calendar-monthly SLO. Add one to enable." The month picker defaults to the last completed calendar month.
The report contains:
- Summary — target, achieved, status (MET / MISSED), total downtime in window
- Incidents — list of incidents during the period with cause and resolution
- Maintenance windows — the maintenance that actually ran during the period, itemised per
occurrence for every window type (recorded from 2026-07-29 onward — see the note under Maintenance and pause exclusion above)
- Methodology — how availability was measured
- Cryptographic checksum for verification
This report is what you send to your customer at month-end to demonstrate compliance.
Getting it out of Enori
Two buttons at the bottom of the report:
| Button | What you get |
|---|---|
| Download CSV | The same figures and the same checksum as a spreadsheet file, ready for Excel or Sheets. A report for an unfinished month is named …-provisional.csv so it can't be mistaken for a final one. |
| Opens your browser's print dialogue on the rendered report. Choose Save as PDF there if you need a PDF. |
There is no separate PDF export — the report is an HTML document, and Print → Save as PDF is how you produce a PDF from it. A one-click PDF download is deliberately not built.
Reports for a month that hasn't finished
You can generate a report mid-period, and Enori will say so rather than pretending otherwise. The picker shows "Period in progress — figures not final", and the report itself opens with an amber banner:
PROVISIONAL — period in progress; figures are not final. August 2026 does not end until September 1, 2026. The figures below cover only the part of the period elapsed at generation time and will change. No audit record was filed for this document, and it is not a Service Level Agreement attestation.
That promise is literal. "Total downtime in window" counts only the downtime that has actually happened so far — it is not the elapsed failure rate projected across the rest of the month. So the figure agrees with the incidents listed beside it, and it will keep rising as the month runs rather than starting high and settling down.
Send the customer the report generated after the period closes; the provisional one is for your own mid-month check.
Publishing SLO compliance on your status page
An SLA report is something you send to one customer. If you'd rather let everyone see the commitment without asking, you can publish it: edit the status page and switch on Show SLO Compliance.
With it on, each monitor on the page lists its SLOs by name, showing what each one has actually achieved against its target — for example:
Customer SLA 99.960% vs 99.900% target
The achieved figure is green when the SLO is being met and red when it isn't (met counts as achieving exactly the target, the same rule your own dashboard uses, so the two never disagree about an SLO sitting precisely on its number). Hovering shows the full sentence. A monitor with several SLOs lists them all.
A few things worth knowing before you turn it on:
- The number is the same one you see on your own dashboard. It's computed live from the same
source, so the public page and your rail card can't drift apart and quietly contradict each other.
- It publishes a commitment. Once it's on, anyone with the link can see the target you set and
whether you're meeting it. That's the point — but set your targets before you publish, not after.
- Monitors without an SLO show nothing extra. No placeholder, no empty row. Same for SLOs you've
disabled: an SLO that isn't measuring publishes nothing rather than publishing a stale number.
- An SLO too young to score publishes no figure. Where the percentage would go, the page prints
the SLO's name followed by "not enough data yet" — and withholds the target too, since a target with no achievement beside it is an unanswered question. This matters most for latency SLOs, which need hundreds of checks before a single slow one is affordable: before this, such an SLO published a fabricated 0.000% in red — a public claim of total failure by a service that had merely not been running long. It never does that now.
- Password-protected pages show nothing until the visitor enters the passcode — not the SLO
rows, not the monitors, nothing but the page name and your brand colour. Once someone unlocks it, they see the SLO compliance like any other visitor, and stay unlocked for 12 hours. The passcode controls who sees the page; it does not hold anything back from someone who's through it.
- The toggle is per status page, so you can publish compliance for a client-facing page and keep it
off on an internal one.
- The RSS incident feed and the status badge carry no SLO data, whatever the toggle says. Only
the status page itself publishes it.
Getting started
The fastest way to get value from SLOs is to set up three on your most important monitor.
1. Open the monitor
Navigate to Monitors → choose your most-trafficked monitor → detail page.
2. Click "+ Add SLO" on the SLO rail card
Look for the SLO card in the right rail. Click the "+" or use the SLO management page (/dashboard/monitors/{id}/slos).
3. Create three SLOs
Following this template:
SLO #1 — Availability (engineering target)
| Field | Value |
|---|---|
| Name | Availability — internal |
| SLI type | Availability |
| Target | 99.9 |
| Window type | Rolling |
| Window length | 30 days |
| Burn-rate alerts | Balanced (Fast + Slow) |
| Exclude maintenance | ✓ |
| Exclude paused | ✓ |
SLO #2 — Latency (UX target)
| Field | Value |
|---|---|
| Name | Latency — under 500ms |
| SLI type | Latency |
| Latency threshold | 500 ms |
| Target | 95 |
| Window type | Rolling |
| Window length | 28 days |
| Burn-rate alerts | Balanced |
SLO #3 — Calendar SLA (if you have a customer agreement)
| Field | Value |
|---|---|
| Name | Customer SLA |
| Description (Rationale) | e.g. "MSA §4.2 — monthly uptime commitment" |
| SLI type | Availability |
| Target | 99.5 (matching your contract) |
| Window type | Calendar |
| Calendar cycle | Monthly |
| Burn-rate alerts | Balanced |
4. Wait
The SLO starts measuring from the moment you create it. The rail card and management page fill in achieved %, burned % and burn rate as data accumulates. Until there are enough checks for the budget to mean anything, the card reads "Not enough data" instead of a percentage — that is expected, not a fault. An availability SLO settles within minutes; a tight latency SLO can need several hundred checks first.
5. Generate your first SLA report (if you set up SLO #3)
Once the month has closed, navigate to the SLO management page and click Generate SLA report. Then either Download CSV, or Print → Save as PDF — and send it to your customer.
When Enori refuses a change
Two SLO rules are enforced at the point of change rather than silently producing a broken SLO. Both apply on the dashboard, the REST API and the MCP tools alike.
Clearing a monitor's expected keyword while a content-match SLO uses it. The request is refused with:
This monitor has a content_match SLO that measures the expected keyword. Clearing the keyword would leave it nothing to check, so the SLO can never register a failure and would read 100% forever. Delete that SLO first, or point it at something else.
The reason is the failure it prevents: with no keyword, every new check records "no keyword result", and as the SLO's window rolls past the last keyword-bearing check its failure count falls to zero. The SLO would sit at a permanent, unmoving 100% green — the worst possible state for a reliability objective, because it looks exactly like success, and it would land in a checksummed SLA report as evidence.
To do it anyway, delete the content-match SLO first (or change its SLI type), then clear the keyword. Three things that are not refused: changing the keyword to a different one (the SLO asserts a keyword is present, not which one), clearing a keyword on a monitor with no content-match SLO, and renaming or otherwise editing the monitor. The refusal also fires when the SLO is switched off — a disabled SLO can be re-enabled straight back into the broken state.
Creating an SLO that can't be measured. A content-match SLO on a monitor with no expected keyword, or a latency SLO on a Domain or Job monitor, is refused at creation with the reason and the remedy.
FAQ
Do I need an SLO for every monitor?
No. Start with the 1–3 monitors that matter most to your business — typically the customer-facing API, the marketing site, and a critical internal service. Adding SLOs to less-important monitors creates noise without benefit.
What happens when an SLO turns red?
The status pill on the rail card and the management page reads Exhausted (it reads At risk from 60% burned onwards, before that). The dashboard's SLO health panel says budget exhausted and budget at risk for the same two states. If burn-rate alerts are enabled, a notification fires through your configured channels. The standard SRE response is a feature freeze on the affected service — focus engineering effort on stability until the budget recovers.
How do I read the rolling vs calendar windows when both exist?
You'll see them side-by-side on the management page. The rolling SLO updates continuously and reflects current state. The calendar SLO is your contract-aligned view; check it at month-end before sending the SLA report.
Why is my burn rate showing 0× when there's downtime?
Burn rate is computed as (burned / elapsed) ÷ (budget / window). If elapsed is very small (just-created SLO) or burned is zero, you'll see 0×. Wait for the window to accumulate data.
What if my monitor was paused for a week?
If "Exclude paused" is enabled (the default), the paused time is removed from the SLO calculation. The SLO behaves as if those 7 days didn't exist. If "Exclude paused" is off, the paused time counts as downtime and burns budget.
Can I edit an SLO after creating it?
Yes. Click the SLO card on the rail or "Edit" button on the management page. All fields are editable. Changing the target retroactively re-evaluates the budget against the new value — there's no data loss.
How do I pause an SLO without deleting it?
Toggle the Enabled switch off in the edit modal — it reads "When off, the SLO is not evaluated", and that is exactly what happens. The SLO stops measuring: no achieved %, no budget, no burn rate, and no alerts.
It stays visible. The rail keeps the SLO listed with a ⚪ Disabled pill and "Not measuring" where the budget line would be, sorted to the bottom below the healthy ones, so a switched-off SLO can't be mistaken for a healthy one at a glance. On the public status page it publishes nothing at all. Your configuration is untouched, so switching it back on resumes measurement immediately.
Useful during planned chaos tests or while diagnosing a noisy SLO. Note that a disabled content-match SLO still blocks you from clearing the monitor's keyword — see When Enori refuses a change.
Can I have multiple SLOs of the same type?
Yes. A monitor can have, for example, three Availability SLOs at 99%, 99.9%, and 99.99% targets. Each is evaluated independently. Useful for tiered alerting — the 99% SLO is your "absolute floor" alert; the 99.99% is your stretch goal.
What if I miss the SLA?
Practically: contact the customer per your contract terms. Operationally: the SLA report will show "MISSED" on the affected period. Use it as the basis for the credit calculation specified in your agreement. Fix the underlying issue, document the root cause, and consider tightening your internal SLO so you have more buffer next time.
Reference: limits and bounds
| Setting | Allowed values |
|---|---|
| Target % | 90.000 — 99.999 (three decimal places) |
| Latency threshold | 1 — 60,000 ms |
| Rolling window length | 7, 28, 30, 90, or 365 days |
| Calendar cycle | weekly, monthly, quarterly, yearly |
| Description / rationale | Up to 500 characters |
| Name | Up to 80 characters |
| SLOs per monitor | 5 (same on every plan) |
| SLOs per account | Base 10 · Pro 50 · Business 200 |
Your account allowance matches your plan's base monitor allowance, so the rule of thumb is "one SLO per monitor your plan includes". Extra monitor slots bought as an add-on raise how many monitors you can run, but not how many SLOs you can create — every SLO joins a 1-minute, a 5-minute and a 10-minute sweep, and that capacity is what the plan cap protects. Creating past either cap returns a 403 telling you which limit you hit.
Related documentation
- API reference — for programmatic SLO management via
/api/monitors/{id}/slos - Alert episodes — how Enori turns burn-rate alerts into actionable incidents
- SLA reports — generating customer-facing compliance documents
- Maintenance windows — declaring planned downtime that's excluded from SLOs
Last updated: 2026-08-16. Feedback or corrections: support@enori.io