All lessons
ArchitectureAdvanced13 min readIn my stack

Reliability, SRE and operations

SLOs and error budgets, on-call, incident response, postmortems and disaster recovery.

  • SLO / SLA
  • Incidents
  • Runbooks
  • Disaster recovery
  • Postmortems
Step 1/6
Checkout takes 8 seconds and users are fuming. Time to call the detectives: metrics, traces and logs.

In 30 seconds

Reliability is the practice of keeping a service working the way users expect, and recovering quickly when it does not. Site Reliability Engineering (SRE) makes it measurable: define what 'working' means (SLIs), set targets (SLOs), and use the remaining error budget to balance new features against stability. Around that sit on-call, incident response, runbooks, postmortems and a tested plan for disasters.

Why I use it

  • I use these practices because the expensive part of an outage is usually the confusion around it, not the bug itself.
  • SLOs turn vague complaints like 'the site feels slow' into numbers a team and a client can agree on.
  • Backups only count if a restore has been tested; I learned to treat untested backups as no backups.
  • Runbooks and postmortems let a small team operate production calmly instead of relying on one person's memory.

How it works

01SLI, SLO, SLA and error budgets

A Service Level Indicator (SLI) is a measurement of user experience, such as the share of requests that succeed, or the share served in under 300 ms. A Service Level Objective (SLO) is the target for that indicator over a window: 99.9 percent of checkout requests succeed over 30 days. A Service Level Agreement (SLA) is a contract with consequences, usually refunds, and should be looser than your internal SLO so you have warning before breaking it. The error budget is the gap between perfect and the SLO: 99.9 percent allows about 43 minutes of failure per 30 days. While budget remains, ship features; when it is spent, prioritise reliability work. Measure SLIs where users feel them (at the load balancer or from synthetic checks), and alert on fast budget burn, not on every CPU spike.

yaml
groups:
  - name: api-slo
    rules:
      - alert: ApiErrorBudgetFastBurn
        # 99.9% SLO: page if errors burn the budget 14.4x too fast
        expr: |
          (sum(rate(http_requests_total{job="api",status=~"5.."}[1h]))
            / sum(rate(http_requests_total{job="api"}[1h]))) > (14.4 * 0.001)
          and
          (sum(rate(http_requests_total{job="api",status=~"5.."}[5m]))
            / sum(rate(http_requests_total{job="api"}[5m]))) > (14.4 * 0.001)
        for: 2m
        labels:
          severity: page
        annotations:
          summary: "API is burning its 30-day error budget too fast"

02On-call and incident response

On-call means someone is responsible for responding when production breaks, with a clear rotation, escalation path and enough rest. It only works if alerts are actionable: every page should mean a user-facing problem that needs a human now, with a link to the dashboard and the runbook. Everything else goes to a ticket queue. When an incident starts, follow a simple sequence. Acknowledge the alert. Assess impact and declare severity. Assign roles: an incident commander who coordinates, someone hands-on, someone communicating with users and stakeholders. Mitigate first: roll back the deploy, fail over, disable the feature flag, scale up, or block the abusive traffic. Find the root cause only after users are safe. Keep a timeline in one channel as you go, and post status updates at a regular rhythm even when nothing has changed.

  • Detect, acknowledge, assess severity, assign roles.
  • Mitigate before you diagnose: rollback is often the fastest fix.
  • Communicate on a schedule; silence makes incidents feel worse.
  • Close with verification, then schedule the postmortem.

03Runbooks and blameless postmortems

A runbook is a short, practical guide for a known situation: 'disk above 90 percent', 'queue backlog growing', 'restore the database to a point in time'. It lists symptoms, the dashboards to check, safe diagnostic commands, the fix, and when to escalate. Good runbooks let a tired engineer at 3 a.m. act correctly, and they are the first step toward automating the fix. After any significant incident, write a postmortem: what happened, the timeline, the impact, how it was detected and resolved, the contributing factors and the concrete follow-up actions with owners and dates. Keep it blameless: ask why the system allowed the mistake rather than who made it, because people hide information when they fear blame. Review follow-ups until done; a postmortem with no completed actions is just a story.

04High availability vs disaster recovery: RPO and RTO

High availability (HA) keeps a service running through the failure of a component: multiple app instances, a database replica ready for failover, redundant load balancers, health checks that route around failures. Disaster recovery (DR) is about returning after something HA cannot absorb: a region outage, data corruption, ransomware, an accidental DROP TABLE that replicates instantly to every replica. Two numbers define DR. The Recovery Point Objective (RPO) is how much data you can afford to lose, measured in time: hourly backups mean up to an hour lost. The Recovery Time Objective (RTO) is how long you can be down while restoring. Lower numbers cost more: continuous WAL archiving and point-in-time recovery for a small RPO, warm standby infrastructure for a small RTO. Keep backups off the server, encrypted and versioned, and restore them on a schedule.

  • Replicas protect against hardware failure, not against bad writes; backups do.
  • Follow the 3-2-1 idea: three copies, two media, one off-site.
  • A restore drill measures your real RTO.

05Chaos testing and day-to-day ops

Chaos testing means injecting failures on purpose to check that the system and the team behave as expected. Start small and safe: in staging, kill an app instance, stop Redis, add latency to a payment API mock, fill a disk, and watch whether alerts fire, retries behave and the service degrades gracefully. Game days do the same with people: rehearse a database restore or a failover with the runbook in hand. Day-to-day operations is less dramatic but matters more: reviewing dashboards and alerts, applying security updates, rotating certificates and secrets, watching disk and database growth, checking backup jobs and restore tests, cleaning logs and old builds, tuning slow queries, and keeping dependencies current. Track toil, the repetitive manual work, and automate the parts that come back every week.

Use it when

  • Any production service with paying users or contractual commitments.
  • Teams that argue about whether to ship features or fix stability; error budgets settle it.
  • Systems holding data that would be costly or impossible to recreate.
  • Before migrations or infrastructure changes, to agree on acceptable risk.

Skip it when

  • Setting 99.99 percent targets for an internal tool where 99 percent is plenty; every nine costs a lot.
  • Heavy process (incident roles, formal reviews) for a weekend prototype.
  • Chaos experiments in production before basic monitoring and rollback exist.

Alternatives

  • Uptime checks only

    A simple external monitor is a fine start for small sites, but it misses partial failures.

  • Managed platforms

    PaaS and managed databases shift some availability and backup work to the provider.

  • ITIL-style operations

    More formal change and incident management, common in large enterprises.

  • Incident tooling

    Paging and status-page services add structure to on-call and communication.

Best practices

  1. Define a few SLIs from the user's point of view and set realistic SLOs.
  2. Page only on actionable, user-facing symptoms; send the rest to tickets.
  3. Link every alert to a dashboard and a runbook.
  4. Mitigate first, diagnose second; make rollback one command.
  5. Write blameless postmortems with owned, dated actions.
  6. Keep backups off-server and test restores on a schedule.
  7. Agree on RPO and RTO with the business and design backups to meet them.

Common mistakes

  • Alerting on every metric, training people to ignore pages.
  • Treating replicas as backups, then replicating a bad delete everywhere.
  • Never testing restores, and discovering a broken backup during a disaster.
  • Debugging live for an hour when a rollback would have fixed users' problem in minutes.
  • Writing postmortems that blame a person and produce no system changes.

Deep dive

06Multi-window, multi-burn-rate alerting

Burn rate is how fast you spend the error budget relative to plan: a burn rate of 1 spends exactly the budget over the SLO window, 14.4 spends a 30-day budget in about two days. The SRE Workbook recommends combining several thresholds, each checked over a long and a short window. Page when 2 percent of the monthly budget is burnt in one hour (burn rate 14.4, confirmed over 5 minutes) or 5 percent in six hours (burn rate 6, confirmed over 30 minutes). Open a ticket when 10 percent is burnt in three days (burn rate 1, confirmed over 6 hours). The long window gives significance, so short blips do not page; the short window makes the alert stop soon after the problem is fixed. Use recording rules to precompute error ratios per window so alerts stay cheap to evaluate.

yaml
groups:
  - name: api-slo-recordings
    rules:
      - record: job:slo_errors:ratio_rate5m
        expr: sum(rate(http_requests_total{job="api",status=~"5.."}[5m])) / sum(rate(http_requests_total{job="api"}[5m]))
      - record: job:slo_errors:ratio_rate1h
        expr: sum(rate(http_requests_total{job="api",status=~"5.."}[1h])) / sum(rate(http_requests_total{job="api"}[1h]))
      - record: job:slo_errors:ratio_rate30m
        expr: sum(rate(http_requests_total{job="api",status=~"5.."}[30m])) / sum(rate(http_requests_total{job="api"}[30m]))
      - record: job:slo_errors:ratio_rate6h
        expr: sum(rate(http_requests_total{job="api",status=~"5.."}[6h])) / sum(rate(http_requests_total{job="api"}[6h]))
  # Page: (1h > 14.4*0.001 and 5m > 14.4*0.001) or (6h > 6*0.001 and 30m > 6*0.001)

07Choosing SLIs well: latency histograms and dependencies

Good SLIs are ratios of good events to valid events, measured as close to users as possible. For latency, use histograms with bucket boundaries at your thresholds, then define the SLI as the share of requests faster than, say, 300 ms; percentiles averaged across instances are mathematically wrong, while bucket counts add up correctly. Exclude what is not your failure (client 4xx errors, health checks) but be strict about what is: a 200 response with an error page is a failure. Consider dependencies: if your service synchronously needs three others at 99.9 percent each, your achievable availability is lower than 99.9 percent unless you add redundancy, caching or graceful degradation. Your SLO must be weaker than the product of your hard dependencies, or you need to remove some of them from the critical path.

  • SLI = good events / valid events, over a rolling window.
  • Latency SLI from histogram buckets, not averaged percentiles.
  • Hard dependencies cap your achievable SLO.

08Safe change: progressive delivery and fast rollback

Most incidents are caused by changes: deploys, configuration updates, migrations, certificate or DNS edits. Reduce the blast radius of each change. Deploy progressively: one instance or a small percentage of traffic first (a canary), compare its error rate and latency with the rest, then expand. Separate deploying code from releasing features with flags, so a bad feature is switched off without a redeploy. Make database migrations backward compatible so the previous app version still works after the schema changes, which keeps rollback possible. Automate rollback on canary failure where you can. Freeze risky changes around peak business events. Track change failure rate and time to restore; they tell you whether delivery is getting safer, and most reliability gains come from here rather than from more hardware.

09Backups that restore: PITR, immutability and drills

For PostgreSQL, point-in-time recovery combines a periodic base backup with continuous archiving of WAL segments, so you can restore to any moment, for example one minute before an accidental DELETE. Tools such as pgBackRest and WAL-G handle compression, encryption, retention and parallel restore to object storage. Protect backups from the same threats as production: use separate credentials that the application servers do not hold, enable object versioning or object lock so ransomware or a compromised server cannot delete them, and copy to a second region or provider. Then measure. A scheduled drill restores the latest backup onto a fresh server, runs integrity checks and a few application queries, and records how long it took. That duration is your real RTO; the age of the newest recoverable data is your real RPO.

bash
# pgBackRest: full backup weekly, differential daily, WAL archived continuously
pgbackrest --stanza=main --type=full backup
pgbackrest --stanza=main --type=diff backup
pgbackrest --stanza=main info
 
# Restore to just before a bad DELETE (run on a fresh server)
pgbackrest --stanza=main --delta \
  --type=time "--target=2026-11-05 14:31:00+00" \
  --target-action=promote restore

Real-world scenarios

01

The team is drowning in alerts

Problem

On-call receives forty pages a week, mostly CPU, memory and single failed health checks that resolve themselves. Real incidents were missed twice because people muted notifications.

How to solve it

Audit a month of pages and label each: needed action now, needed action later, or no action. Delete or downgrade everything in the last two groups to tickets or dashboards. Define two or three SLIs per user-facing service (success rate, latency) and replace cause-based pages with multi-window burn-rate alerts. Require every remaining page to link a runbook and dashboard. Review page volume weekly in the team meeting with a target, such as two pages per on-call shift at most, and treat repeated pages as bugs to fix.

02

An accidental DELETE in production

Problem

A maintenance script without a WHERE clause deleted most rows of a customers table. The replicas applied it within a second.

How to solve it

Stop writes that could build on the damage, for example by putting the app in maintenance mode for affected features. Do not restore over production. Restore the latest base backup plus WAL to a separate server with a target time just before the DELETE, verify the data, then copy the missing rows back into production with an INSERT ... SELECT or a dump of that table, reconciling rows created afterwards. Communicate impact while you work. In the postmortem, add guards: scripts run in transactions with row-count checks, and production access through reviewed procedures.

03

Agreeing reliability targets with a client

Problem

A client asks for 'five nines' on a booking platform hosted on one VPS with a small team and a fixed budget.

How to solve it

Translate nines into downtime and cost: 99.999 percent allows about 26 seconds per month, which needs multi-region redundancy and 24/7 staffing. Then ask what really hurts the business: failed bookings at peak hours, lost data or slow pages. Propose SLOs on those flows, such as 99.9 percent successful bookings monthly with an RPO of five minutes and an RTO of one hour, and show what each step up costs (second server, managed database, on-call rotation). Agree on an error budget policy so both sides know what happens when it runs out.

Cheat sheet

Error budget minutes (30 days)
99% = 432 min, 99.9% = 43.2 min, 99.95% = 21.6 min, 99.99% = 4.3 min
Fast-burn page
Burn rate 14.4 over 1h AND over 5m (2% of budget)
Slow-burn page
Burn rate 6 over 6h AND over 30m (5% of budget)
Ticket, not page
Burn rate 1 over 3d AND over 6h (10% of budget)
Burn rate
observed error ratio / (1 - SLO)
Check backups
pgbackrest --stanza=main info
Point-in-time restore
pgbackrest --stanza=main --type=time "--target=..." restore
Incident roles
Incident commander, operations lead, communications lead
Postmortem must have
Timeline, impact, contributing factors, owned and dated actions

In my real projects

  • The POS SaaS server runs a Prometheus, Grafana and Loki observability stack with health checks, runbooks and backup scripts alongside the Docker Compose deployment.
  • My baseline for every server includes snapshots plus tested restores, and monitoring of disk, load and uptime through Uptime Kuma or provider alerts.
  • I diagnose production issues read-only first, from logs, before changing anything; that is how I fixed a localization middleware bug, an image conversion bug and a cache-permission error.
  • For server consolidation I follow inventory, copy, verify, switch DNS, and keep the old server until everything is verified; on one server I brought disk usage down from 91% to 66%.

Questions & answers

Q1What is the difference between an SLO and an SLA?

An SLO is an internal reliability target for a measured indicator, for example 99.9 percent of API requests succeeding over 30 days. It guides engineering decisions. An SLA is an external contract that promises a level of service and defines consequences, typically service credits, if you miss it. The SLA should be looser than the SLO, so you see trouble internally and react before you owe customers anything. Both depend on SLIs, the actual measurements, which should reflect what users experience, such as success rate and latency at the edge, rather than server CPU.

Q2What is an error budget and how do you use it?

It is the amount of unreliability your SLO allows. A 99.9 percent monthly availability target permits about 43 minutes of failure in 30 days. The budget turns reliability into a shared decision: while budget remains, the team ships features and takes reasonable risks; when it is nearly spent, the team slows releases and prioritises stability work such as fixing flaky dependencies or improving rollbacks. It also drives alerting: instead of paging on raw error counts, you page when the budget is burning fast enough to be exhausted soon, which cuts noise significantly.

Q3Walk me through how you handle a production incident.

First I acknowledge the alert and check impact: which users, which features, since when. I declare a severity and, if it is serious, separate roles so one person coordinates and communicates while others work hands-on. Mitigation comes before root cause: roll back the latest deploy, disable a feature flag, fail over or scale up, whatever restores service fastest. I keep a timeline in one channel and send regular updates to stakeholders. Once stable, I verify with metrics, close the incident, and schedule a blameless postmortem with concrete actions, owners and deadlines.

Q4Explain RPO and RTO with an example.

RPO, the recovery point objective, is how much data loss is acceptable, expressed in time. RTO, the recovery time objective, is how long the service may be down while you recover. If a shop takes nightly backups only, its RPO is up to 24 hours: a failure in the evening loses the whole day's orders. With continuous WAL archiving and point-in-time recovery, RPO drops to minutes. If restoring a 200 GB database onto a fresh server takes three hours, that is the RTO, unless you keep a warm standby. The business sets the targets; engineering prices them.

Q5What is the difference between high availability and disaster recovery?

High availability keeps the service up through routine component failures: several app instances behind a load balancer, a standby database that takes over automatically, redundant networking. Users ideally notice nothing. Disaster recovery covers events HA cannot absorb: losing a whole data centre, corruption, ransomware, or a bad migration that replicates to every replica instantly. HA is about redundancy in real time; DR is about restoring from independent copies, defined by RPO and RTO. You need both, because a perfectly replicated database will happily replicate a mistaken DELETE.

Q6What makes a good alert?

A good alert is actionable, urgent and tied to user impact. It fires on a symptom users feel, such as rising error rate, slow checkouts or SLO burn, not on a cause that may be harmless, like CPU at 80 percent. It has a clear title, says how bad it is, links to the relevant dashboard and runbook, and goes to the right person. If an alert regularly fires without anyone needing to act, it should be tuned, downgraded to a ticket or deleted. Noisy alerting is dangerous because people learn to ignore pages, including the real one.

Senior-level questions

S1How would you write an error budget policy, and what happens when it is exhausted?

The policy is a short document agreed in advance by engineering and product, so decisions under pressure are not improvised. It states the SLOs, the window and who owns them. It defines thresholds: when the budget is healthy, normal releases; when more than half is spent early in the window, extra review for risky changes; when exhausted, a feature freeze for that service except for reliability fixes and security patches until the rolling window recovers. It lists exceptions, such as outages caused by a dependency covered by its own SLO, and an escalation path if teams disagree. The trade-off is explicit: product accepts slower delivery after unreliability, engineering accepts shipping fast while the budget allows, and both use the same numbers.

S2How do you design for a region or provider outage on a modest budget?

Start from RPO and RTO, not from architecture. If the business accepts an hour of downtime and a few minutes of data loss, a full active-active setup is unnecessary. A cost-effective pattern is pilot light: continuous WAL archiving and object storage replication to a second region or provider, infrastructure defined as code, container images in a registry reachable from both, and DNS with a low TTL. Failover then means provisioning servers from code, restoring the database to the latest point and switching DNS. Practise it twice a year and time it. Warm standby with a running replica cuts RTO to minutes at higher cost; active-active cuts it further but forces you to solve multi-region writes, which is a large and permanent complexity.

S3What does a mature incident review process look like beyond the postmortem document?

It is a learning loop rather than paperwork. Incidents are declared generously and cheaply, so small ones are captured too. Reviews happen within days while memory is fresh, with a facilitator who was not involved, focusing on how the situation made sense to people at the time and which signals were missing. Actions are few, specific and owned, prioritised in normal planning, and tracked to completion with a regular report of overdue items. Patterns across incidents are reviewed quarterly: recurring causes, slow detection, painful runbooks. Metrics such as time to detect, time to mitigate and repeat incidents show whether the loop works. The trade-off is engineering time spent on reviews, which pays back by preventing the same outage from happening twice.

Glossary

SLI
A measured indicator of user experience, such as the share of fast, successful requests.
SLO
An internal target for an SLI over a time window, like 99.9 percent over 30 days.
Error budget
The amount of failure an SLO allows, used to balance risk and feature work.
Burn rate
How fast the error budget is being consumed compared with an even pace.
MTTR
Mean time to restore: how long, on average, it takes to recover service after failure.
Canary release
Sending a small share of traffic to a new version before rolling it out fully.
PITR
Point-in-time recovery: restoring a database to an exact moment using backups and logs.
Toil
Manual, repetitive operational work that scales with load and can be automated.
Game day
A planned exercise where a team practises responding to a simulated failure.

Have a product in mind?Let's build it.

Tell me about your idea, your product or your team. I usually reply within one working day.