HIVE80lab — Ops notes

The database outage runbook: protect the data first, fix the second

A database outage is different from every other outage in one way: the thing you are tempted to do to fix it — restart, force-recover, restore — is the thing most likely to destroy the data. An nginx restart that goes wrong costs you five minutes. A database recovery that goes wrong costs you the day's orders. This runbook has one ordering rule above everything else: confirm what you are looking at, stop the writes, degrade honestly, then restore with a plan. Where DNS outages are quiet (see the DNS outage runbook), database outages are loud — your app will tell you, in its own error language, exactly where it hurts. The runbook is about listening before acting.

1. First ten minutes: confirm it is actually the database

Half of "database down" incidents are something else wearing its clothes. One triage table, four checks:

SymptomCheck (2 minutes each)Verdict
App errors mention connection refused, too many connections, or timeoutsRead the app logs' error class; connect from a second host with a raw client (psql/mysql)Connects from second host = network or pool config. Refused everywhere = the database host or process
Every page 500s, including the marketing pagesLoad a static asset directlyStatic works = data layer. Static also broken = network, DNS, or disk — wrong runbook, switch to the internet outage runbook
Site works but is seconds-slowCheck for lock waits and long-running queriesHung or contended, not down — restarting is the wrong move (section 2)
Database host disk near 100%df -h on the database hostIt was never a mystery: follow the disk-full runbook — full disks are the most common small-team database outage

The reason for the two-minute checks: the fix for a dead database is a restore, the fix for an exhausted connection pool is a config change, and the fix for a full disk is deleting files. Run the wrong one and you make all three problems.

2. Three failure modes, three different responses

ModeLooks likeResponse
DownConnections refused; process absent or crash-loopingRead-only degraded mode now (section 4), then the restore path (section 5). Do not restart in a loop — capture state first (section 3)
HungApp threads stuck waiting; too many connections; queries that never finishFind the blocker query (pg_stat_activity / SHOW PROCESSLIST), kill selectively, fix the pool config. A restart "fixes" it by evicting every customer mid-write
SlowEverything works, p95 latency in secondsUsually a deploy shipped a bad query plan or a missing index; find the new slow queries, roll back the deploy if the cost is high. Slow is a limp, not a fire

The mode matters because the instinct is the same for all three — "restart the database" — and the instinct is only right for one of them. Write the mode verdict into the incident channel before acting; it forces the two minutes of thought.

3. Stop the writes before you touch anything

A database you cannot read from is an outage — painful, recoverable. Data you corrupt while recovering is a catastrophe. The ordering rule for every response:

This is where the incident commander earns the title: one person owns the stop-write decision so it is made once, loudly, instead of by whoever is most panicked.

4. Degraded mode: the subset that still works

Customers forgive "part of the site is down" far more than "the site lies." Degraded mode is a decision, not an accident — pre-decide the subset:

The one place degraded mode is wrong: if the fallback would write to a second system of record, you are building a data fork you will pay for at reconciliation time. Queue the writes; serve the reads; never split the truth. Degraded mode pairs with the payment outage playbook when the degraded slice is revenue itself.

5. The restore you rehearsed

The restore path is only fast if it was walked in peacetime. The small-team version, start to finish:

6. After it is back: the ten-minute verification pass

Restored does not mean healthy. Five checks before declaring victory:

CheckHealthy looks like
Connection count and pool configBack to normal levels; pool size matches what the app actually needs (the most common recurrence)
Replication lag (if you have replicas)Zero before shifting read traffic back — otherwise you serve stale data and call it recovered
Slow query logLeft on for 24 hours; the outage's root cause usually leaves fingerprints here
Disk headroomHeadroom confirmed and an alert set — the full disk comes back otherwise
The queue, reconciledEvery queued order processed and every customer acknowledged — the degraded-mode promise kept to the last one

Then close it the way every incident should close: the post-mortem template for the review, and the action item tracker for the fixes — the pool config change, the disk alert, the PITR checkbox become tracked rows, not meeting memories.

7. Common mistakes

Takeaways

From the HIVE80lab kit

Related: the severity matrix decides who gets woken up, the vendor outage runbook covers the managed-database provider's bad day, and the uptime & downtime budget decides how much resilience this runbook deserves.