SLOs for small teams are not a shrunken version of Google’s SRE book — they are one to three targets on your single most user-facing service, measured from the user’s side, with an error budget you spend to decide when to slow down. You do not need an SRE function, a platform team, or a dashboard wall. You need a couple of numbers your team actually acts on. That is the entire discipline, and a 10-to-80-engineer regulated team can stand it up in an afternoon.
Two of the guides in this library lean on SLOs without stopping to define them. On-call without burnout tells you to page only on symptoms that are user-facing and actionable — an SLO is how you decide what “user-facing” objectively means. GitOps and progressive delivery gates every canary on an SLO check and rolls back automatically when it fails — that is an SLO doing its job at machine speed. This article is the missing definition underneath both, sized for a team with no SRE title on the org chart.
What are SLOs, and do small teams need them?
Yes — a light version, and the concept matters more than the machinery. Three terms, in order:
- SLI (Service Level Indicator): something you measure about the service from the user’s perspective. The share of requests that succeed. The p95 request latency. A measured number, nothing more.
- SLO (Service Level Objective): a target for that indicator over a window. “99.9% of requests succeed, and p95 latency stays under 300ms, over 28 rolling days.” The SLI is the number; the SLO is the line you draw across it.
- Error budget: 1 minus the SLO. At a 99.9% target, 0.1% of requests are allowed to fail. That allowance is not a failure — it is a resource you get to spend on risk.
Small teams need the concept, not the organization. The two failure modes are equal and opposite. Skip SLOs entirely and on-call pages on noise, because nobody ever defined what “broken” means, so every blip is a judgment call at 2 a.m. Import the full enterprise SRE apparatus and you get an SLO for every microservice, a wall of dashboards, and a ritual nobody consults. The middle — one to three SLOs on the service that matters, wired into how you deploy and how you page — is the whole win, and it is reachable this quarter.
SLO vs SLA vs SLI — what’s the difference?
An SLI is what you measure, an SLO is the internal target you hold yourself to, and an SLA is the contractual promise you make a customer. They are frequently blurred, and the blur is expensive.
| Term | What it is | Who it’s for | Teeth |
|---|---|---|---|
| SLI | A measured indicator (success rate, latency) | Your engineers | None — it’s just a number |
| SLO | Internal target for an SLI over a window | Your team | Error budget; gates deploys and on-call priorities |
| SLA | Contractual promise to a customer | Customers, sales, legal | Financial penalties for a miss |
The operative rule: set the SLO stricter than any SLA you sign. If you promise a customer 99.5%, run an internal SLO of 99.9%, so your error budget burns and warns you well before the contract is at risk. Most regulated SMBs have SLOs long before they have a signed SLA — and that is the correct order. Get the internal target working first; the contractual number is a business decision layered on top of a system you already understand.
Which SLIs and SLOs should a small team start with?
Start with one to three, all on your single most user-facing service, all measured from the user’s side. Here is the starter menu. Take the first row as non-negotiable, then add at most two more.
| SLI (measured at the edge) | Starter SLO (28-day rolling) | Why this one |
|---|---|---|
| Request success rate — non-5xx share of responses at the load balancer | 99.9% of requests succeed | The core “is it working for users” signal. Measured at the LB, not inside the app, so it catches the app being entirely down — the case an in-process metric never reports because the process isn’t running to report it. |
| Latency — p95 request duration at the edge | p95 < 300ms | ”Up but slow” is still broken. p95 catches the tail users actually feel without chasing p99.9 noise on a low-traffic service. |
| Critical-journey availability — synthetic check of one real flow (login, checkout) | 99.9% of synthetic runs pass | Catches whole-path failures a per-request rate misses: auth broken, a dependency down, a page that returns 200 but renders nothing. |
| Freshness — a business-critical async job (export, billing, ETL) completes on time | 99% within its expected window | The one background pipeline whose lateness a customer or an auditor actually notices. Skip this row if you have no such job. |
The first row earns its place on every service. Add latency if users wait on synchronous responses. Add the synthetic check if a login or checkout path is the product. Then stop — three is plenty for a team this size.
What does not belong in this table is as important as what does. Host CPU%, memory pressure, requests per second, and the uptime of a single box are diagnostics, not indicators of user pain. A box can sit at 90% CPU while every user is perfectly happy, and idle at 10% while the service returns 500s to everyone. These are vanity SLIs: they generate green dashboards and false confidence. Keep them for debugging an incident; never make them an SLO.
What’s a good availability target?
99.9% is the right default for almost every SMB service. Three nines is achievable without a follow-the-sun rotation, and it buys a downtime budget your team can actually operate inside. The number that matters is not the count of nines — it is the failure the target permits over your window:
| SLO | Allowed failure (28-day window) |
|---|---|
| 99% | ~6.7 hours |
| 99.9% | ~40 minutes |
| 99.95% | ~20 minutes |
| 99.99% | ~4 minutes |
Look at the 99.99% row and ask, honestly, whether a 10-to-80-engineer team can detect and fix an incident inside four minutes without staffing around the clock. You can’t, so don’t promise it — a target you can’t meet is worse than an honest one, because it burns credibility every month. Resist the opposite temptation too: 100% is not a target, it’s a way to freeze all shipping in the name of a number you’ll never hit. Set availability where your team can genuinely respond, and keep it consistent with your on-call rotation — the SLO defines what the pager is allowed to wake someone for, so the two have to agree.
How do error budgets actually work?
The error budget is 1 minus the SLO, and the point is that you spend it. A healthy budget is permission to take risks — ship faster, run the schema migration, deploy on a Friday. A budget you’re burning through says the reverse: freeze risky changes and spend what’s left on stability until it recovers. Two rules, written down, turn the abstraction into behavior:
- Budget healthy (say, more than half remaining over the 28-day window): normal velocity. Canaries auto-promote, migrations proceed, the team ships.
- Budget burning fast or exhausted: freeze non-essential deploys, no risky migrations, and on-call’s job shifts from features to reliability until the budget climbs back.
This is not theoretical for anyone running GitOps. It is exactly the SLO-gated canary logic already in a progressive rollout: every deploy shifts a slice of traffic, runs an analysis against the SLO, and aborts itself when the new version would burn budget too fast. The error budget is that same rule applied at human speed — the machine enforces it per deploy, the team enforces it per week.
The other half is how you alert. The mistake is paging on the raw SLI — “success rate dipped below 99.9% for one minute.” That fires constantly, and a team trained to ignore a noisy alert will ignore the one that matters, which is the precise trap on-call without burnout is written to avoid. Alert on burn rate instead: how fast you are consuming the window’s budget. A page then means “at this rate you’ll blow the month’s budget before it resets” — rare, real, and worth waking up for. A brief blip that barely dents the budget is a line on a dashboard, not a 2 a.m. wakeup.
How do you measure an SLO without an SRE team?
You already have the data — you need the load balancer’s metrics and one synthetic check, not a platform hire. Three practical rules:
- Measure at the edge. Your ALB, NLB, or ingress already emits per-request status and latency. That is your success-rate and p95 SLI for free, and critically it’s from the user’s side of the application, which is the entire point. Host-level metrics measure the wrong thing from the wrong vantage.
- Add one synthetic check. A scheduled probe that runs a real user journey end-to-end catches the failures request metrics miss — the login that succeeds but the dashboard renders blank. Any uptime or synthetic-monitoring tool does this; you don’t build it, you configure it.
- Use a 28-day rolling window. Not a calendar month (the reset hides problems that straddle the boundary), not 7 days (too twitchy to trust). Twenty-eight days is four weeks of the same weekday mix, and because it rolls, the budget is always “the last four weeks” — which is how humans actually reason about whether things have been healthy lately.
This all assumes the observability floor exists: request metrics scraped and stored somewhere you can query them. That floor is part of what “production-ready” actually means for a cluster, and standing up SLOs is one of the reasons a real platform engineering foundation exists in the first place. If you don’t have request metrics yet, that’s the prerequisite — and it’s cheap, not a project.
The good-enough bar
A small team has good-enough SLOs when three things are true: one to three SLOs exist on the most user-facing service, all measured from the user’s side; a 28-day error budget is defined and visible; and there is a written rule for what happens when the budget burns — deploys slow, on-call priorities shift. That’s the bar. No dashboard wall, no SLO for every service, no SRE title required.
The wrong version is either extreme. No SLOs means every alert is a guess and every “are we down?” is a debate. An SLO spreadsheet nobody opens is theater. The honest test is behavioral: if a burning error budget doesn’t change a single decision — doesn’t freeze a deploy, doesn’t reprioritize on-call — then you don’t have an SLO, you have a chart. Delete it and start over with one number the team will act on.
The Template
A starter SLO worksheet. Fill one block per SLO; a small team has one to three of these total, not one per service. Publish it where on-call can find it, and revisit it quarterly.
One block per SLO (aim for 1-3 total)
- Service: ________________ (your single most user-facing service first)
- SLI — what you measure, from the user’s side: ________________
- Measured where: ☐ load balancer / ingress ☐ synthetic check ☐ ____________ (not host CPU, not RPS)
- Target (SLO): ______% over a 28-day rolling window (and/or p95 < ______ ms)
- Error budget: 100% − SLO = ______ (e.g. 99.9% → 0.1% ≈ 40 min / 28 days)
- Burn-rate alert fires when: on track to exhaust the budget before the window resets — NOT on every dip
- When the budget is HEALTHY (> ____% left): ☐ ship normally ☐ canaries auto-promote ☐ migrations proceed
- When the budget is BURNING / EXHAUSTED: ☐ freeze risky deploys ☐ no migrations ☐ on-call pivots to reliability
- Owner — who watches this number: ________________
- Reviewed quarterly: ☐
Two rules that keep the worksheet alive rather than decorative:
- Start with one. The most user-facing service, one success-rate SLO, one error budget. Add the second only after the first has changed a real decision. A single working SLO beats four aspirational ones.
- If it never changes a decision, delete it. An SLO that never freezes a deploy or greenlights a risk is not measuring anything you use. Cut it, and put the attention on the number that does.
Get one SLO to the point where a burning budget actually slows the team down and a healthy one speeds it up, and you have the whole discipline — the same loop the GitOps rollout runs automatically and the on-call system leans on, minus the platform team you were told you needed.