Postmortem · Oct 10 – 13, 2026
Three days of database overload, and the failures it set off
- Started
- 00:22 UTC
- Saturday, Oct 10, 2026
- Ended
- 08:41 UTC
- Tuesday, Oct 13, 2026
- Duration
- 80 h 19 min
- From the first failure to recovery
- Worst
- Full outage
- 2 h 10 min in 3 stretches
From Oct 10 our main database ran at its limit. Each slowdown turned into timeouts, each timeout into retries, and the retries into more load, so the overload fed itself. A new connection check that disconnected providers whenever the database was slow made it worse, and late on Oct 11, with our load balancers collapsing in turns, traffic fell to about a tenth of normal. The database was out of its overload loop by 08:09 on Oct 12, but that evening a key rotation broke new sign-ins and the connection pooler stalled. Two planned database restarts on Oct 13 caused the last disruptions, and service was back to normal at 08:41.
TimelineAll times UTC
- Healthy
- Degraded
- Partial outage
- Major outage
- Full outage
Connected providers: the readings
| Time (UTC) | Connected providers |
|---|---|
| Oct 10, 01:52 | 88.8k |
| Oct 11, 02:00 | 45k |
| Oct 11, 03:50 | 46k |
| Oct 11, 14:59 | 50k |
| Oct 11, 16:17 | 71.8k |
| Oct 12, 01:00 | about 68k (65–72k until 19:40) |
| Oct 12, 14:39 | about 70k |
| Oct 12, 14:49 | about 45k (planned fleet restart) |
| Oct 12, 14:53 | about 70k |
| Oct 12, 19:40 | about 69k |
| Oct 12, 19:55 | about 33k |
| Oct 12, 20:09 | 69.1k |
| Oct 12, 20:13 | about 72k |
| Oct 13, 06:48 | about 1.9k (the failed restart) |
| Oct 13, 06:54 | 68k |
| Oct 13, 07:12 | 66.5k |
| Oct 13, 07:37 | 69k |
| Oct 13, 08:25 | about 32k (slow start after the second restart) |
| Oct 13, 08:41 | 70k |
| Oct 13, 11:00 | 74k |
| Oct 10, 04:13 – Oct 11, 02:00 | swung between about 15k and 87k |
- Full outage2 h 10 min
- Major outage35 h 35 min
- Partial outage19 h 34 min
- Degraded23 h
Phases
- Oct 10, 00:22 – Oct 10, 04:13Partial outage3 h 51 minBest-available and country provider lists came back empty, and providers began dropping off in bursts.
- Oct 10, 04:13 – Oct 11, 03:35Major outage23 h 22 minHealthy providers were disconnected in bursts whenever the database was slow, and many people could not connect.
- Oct 11, 03:35 – Oct 11, 16:17Partial outage12 h 42 minA fix for the disconnects shipped and providers came back, while the database stayed overloaded.
- Oct 11, 16:17 – Oct 11, 18:00Degraded1 h 43 minProviders were back near 72,000; the database was still saturated.
- Oct 11, 18:00 – Oct 11, 23:34Major outage5 h 34 minLoad balancers collapsed in turns, each down about 5 minutes in every 10.
- Oct 11, 23:34 – Oct 12, 01:17Full outage1 h 43 minTraffic fell to about a tenth of normal.
- Oct 12, 01:17 – Oct 12, 05:24Major outage4 h 07 minLoad balancers kept collapsing until a fix reached all of them.
- Oct 12, 05:24 – Oct 12, 08:09Partial outage2 h 45 minThe database was still overloaded and many connection attempts failed.
- Oct 12, 08:09 – Oct 12, 17:46Degraded9 h 37 minThe database left its overload loop and traffic was back to normal, with short stalls.
- Oct 12, 17:46 – Oct 12, 19:45Major outage1 h 59 minNo new sign-in or session refresh worked, and hosted proxies timed out on 12 of 20 proxy groups.
- Oct 12, 19:45 – Oct 12, 20:08Full outage23 minThe connection pooler stalled and almost no new connections were accepted.
- Oct 12, 20:08 – Oct 12, 20:17Partial outage9 minThe pooler was restarted and providers came back.
- Oct 12, 20:17 – Oct 12, 21:10Degraded53 minThe remaining proxy groups were restarted one at a time.
- Oct 12, 21:10 – Oct 12, 21:17Partial outage7 minThe pooler stalled again and was fixed within minutes.
- Oct 12, 21:17 – Oct 13, 06:45Degraded9 h 28 minShort database freezes, seconds long, while the fix was prepared.
- Oct 13, 06:45 – Oct 13, 06:49Full outage4 minA planned database restart failed and was rolled back.
- Oct 13, 06:49 – Oct 13, 08:08Degraded1 h 19 minProviders were back while the restart was planned again.
- Oct 13, 08:08 – Oct 13, 08:41Major outage33 minThe second restart worked, but new database connections started slowly.
Key moments
- Oct 10, 01:57Providers start being disconnected whenever the overloaded database misses a new 2-second session check.
- Oct 10, 16:36Contracts are found not to be closing. The backlog dates back to Sep 28 and is adding load of its own.
- Oct 10, 22:44Reports arrive that providers struggle to connect and people can't reach them.
- Oct 11, 03:26The disconnect fix ships: an unanswered check is retried instead of ending the connection.
- Oct 11, 18:00Two load balancers start collapsing in turns under slow TLS handshakes and retry storms.
- Oct 12, 02:36A load-balancer build with a faster key exchange starts rolling out. The collapses stop by 05:24.
- Oct 12, 08:09The database is out of its overload loop, and traffic is back at its usual level.
- Oct 12, 17:46A key rotation breaks new sign-ins: servers started before it reject every sign-in issued after it.
- Oct 12, 19:45The connection pooler stalls and almost no new connections are accepted. It is restarted at 20:06–20:08.
- Oct 13, 08:08A second planned database restart succeeds. New database connections start slowly until 08:40, and service is normal by 08:41.
Who was affected
People using the apps. Connections were unreliable for most of three days. At the worst point, late on Oct 11, traffic fell to about a tenth of normal. From 17:46 to 20:13 on Oct 12 almost nobody could sign in or refresh a session; sessions already running kept working until they needed a refresh.
Providers. Providers were disconnected about 3 million times on Oct 10 and 11, in bursts. Most reconnected on their own; operators of some third-party miner builds had to restart them by hand. Separately, a bug in recent SDKs re-sent undeliverable return traffic without end, an estimated 10 TB of provider bandwidth from Oct 2 to Oct 9.
Hosted proxy customers. From 17:46 on Oct 12, setting up proxy windows timed out on 12 of 20 proxy groups until the groups were restarted that evening.
What went wrong
The database fed its own overload
Several independent sources of load grew while the database was already slow: a backlog of contracts that had stopped closing, connections thrown away and reopened on every timeout, old app versions retrying on fixed timers, and scans that only became expensive once a task queue held 117 million rows. Each timeout created more work, so the load fed itself.
A connection check that failed closed
A new check renewed every connection's authorization each minute with a 2-second database budget. On a saturated database the check went unanswered, and an unanswered check closed the connection: about 3 million provider disconnects, each followed by a reconnect that hit the same database.
Load balancers that collapsed under their own retries
Our load balancers used a slow key exchange for every new TLS connection. Under load the handshakes got slower, clients redialed harder, and health checks moved each collapsing host's traffic onto the other one.
A key rotation nobody knew was a key rotation
Renewing a certificate also changed a key that new sign-ins depend on. Servers started before the change kept the old key and rejected every new sign-in until they were restarted.
A connection pooler that couldn't refill
During the restarts a short database stall emptied most connection pools, and the pooler's queue limit was longer than our servers' own timeouts, so every new connection went to a request that had already given up. The same bug came back on Oct 19, and it is fixed at its source there.
Changes made during the response
Several of our own changes during the response caused new failures: a cap that made the contract closer skip work, a database-heavy rollout step, restart waves, the certificate renewal, and a cache rebalance that silently dropped proxy subscriptions.
What we changed
Each fix links to the public commits that made it. Changes to our servers' configuration are described without links.
- DoneConnection checks retry instead of disconnecting, a check no longer re-runs for every connection in a network, and only a definite refusal ends a session.
- urnetwork/server · 6688331 ↗Merge fix/session-lease-unavailable-retry
- urnetwork/server · 7bba6cd ↗Merge fix/session-listener-fanout
- urnetwork/server · 963c4a5 ↗Merge keeping admitted transports through unanswered lease rechecks
- DoneContracts close again: longer scan deadlines, closes batched per payer, progress that survives a timeout, and a background closer that writes at half the rate.
- urnetwork/server · 7e380ef ↗Merge the 24 hour startup contract scan deadline
- urnetwork/server · 6507361 ↗Merge scheduled close owner batches and the per-instance claim bound
- urnetwork/server · e61ed84 ↗Merge the removal of the queued scheduled close claim bound
- urnetwork/server · 6e415d8 ↗Merge deferred expiry rows and session maintenance progress
- urnetwork/server · c0e5f33 ↗Halve the background closer's write concurrency
- DoneServers keep their database connections when a caller gives up, instead of throwing them away and opening new ones.
- urnetwork/server · bec961c ↗Merge keeping pooled connections through caller stops at BEGIN
- urnetwork/server · 037d68c ↗Merge keeping pooled sessions when a caller stops during a transaction statement
- DoneRetries from old app versions are answered cheaply: one read for a missing contract, remembered refusals, and a one-minute hold on refused connections.
- urnetwork/server · 95e8c09 ↗Merge one-read companion origin fallback for non-companion requests
- urnetwork/server · 411de53 ↗Merge remembering definitive credential refusals and closing refused handshakes in band
- urnetwork/server · 64dac87 ↗Merge holding a refused old-build websocket for a minute before its authorization close
- urnetwork/server · 20266ee ↗Merge keeping terminal contract route refusals and deciding missing origins first
- urnetwork/server · a204771 ↗Merge answering recorded contract refusals before the state query and keeping missing-origin refusals 60s
- urnetwork/connect · 23b3b0f ↗Merge holding a definitively refused credential and reporting it to its owner
- DoneA cache script whose cost grew with the square of the connections behind one address is bounded.
- urnetwork/server · ec43360 ↗Merge bounding the forward egress lease script to the owner it updates
- DoneHosted proxies pace their re-dials to an unreachable provider and reuse its identity, and each proxy process registers a bounded number of new clients.
- urnetwork/connect · 5f71903 ↗Merge pacing re-dials of an unreachable fixed destination and reusing its identity
- urnetwork/connect · ce63fe2 ↗Merge keeping a reused fixed-destination identity's key across re-dials
- urnetwork/server · d496def ↗Merge bounding hosted derived-client registrations per proxy process
- DoneQueued work is retired with its contracts, task workers start without scanning the whole queue, and leftover index copies from interrupted maintenance are cleaned up.
- urnetwork/server · 098dc9f ↗Merge retiring queued scheduled closes with their contracts
- urnetwork/server · d409027 ↗Merge keeping the retired-task reap out of taskworker startup scheduling
- urnetwork/server · 1b567df ↗Merge bounding the taskworker queue metrics refresh on the server
- urnetwork/server · c292be1 ↗Merge cleaning up interrupted reindex copies and guarding the rotation by load and size
- DoneLoad balancers use a faster key exchange, and the collapses stopped.
- urnetwork/warp · f30f6fc ↗Merge fix/lb-x25519-curve: LB TLS prefers X25519 key exchange
- DoneA configuration push no longer restarts running servers across the fleet.
- urnetwork/warp · 4cd6bf7 ↗Merge fix/config-restart-no-hold: a restart: false config restarts no running block
- DoneServers re-read sign-in keys they don't recognize, and a new key waits before it is used, so a rotation can't lock out new sign-ins.
- urnetwork/server · 95e2ce2 ↗Merge rereading JWT keys on an unknown kid and letting new signing keys settle
- DoneProxies subscribe again when a cache rebalance moves their channels, and keep watching after a failed read.
- urnetwork/server · 466a006 ↗Resubscribe sharded pub/sub channels after their slot moves
- urnetwork/server · 24feb5f ↗Keep the proxy client watch and reconcile running after a raised change read
- DoneNew alerts for connection pools with requests queued and no database connection.
- urnetwork/warp · a90bade ↗Merge alerting when a PgBouncer pool has clients queued and no server
- DoneDatabase write and memory settings tuned to end the short freezes, including the restart on Oct 13.
- In progressRecent SDKs bound how long they re-send undeliverable return traffic. The fix reaches providers with the next app and SDK release.
- urnetwork/connect · e48e4bb ↗Merge bounding retained return retransmission to a silent destination
Still open
- OpenThe app and SDK fixes above reach people and providers only with a new release. Until then, older versions keep retrying on fixed timers.