The data survives the upgrade. The database around it does not.
How production teams move a live relational database onto a new major engine version, and which state the upgrade silently drops.
Reconstructs major-version database upgrades from GitLab's public change and incident tracker, Zalando's Spilo automation, Supabase's platform docs and PostgreSQL's own commit history. A reader leaves with the dropped-state inventory (planner statistics, sequence values, replication slots, collation-ordered indexes), the conditions that pick in-place pg_upgrade versus a parallel cluster over logical replication, a five-incident failure catalogue in which the cutover is never the outage, and a campaign design where the abort is a priced, rehearsed outcome.
The engine itself committed and then reverted the fixes for two of the four dropped-state traps (sequence replication in 2022, per-index collation tracking in 2021), which is why every operator in the record independently maintains the same workaround scripts; meanwhile the only Sev1 in three years of GitLab upgrade campaigns was a one-character typo aimed at the scaffolding, not the cutover.
What you get out of it
- The upgrade carries the table bytes and drops the derived state: planner statistics (fixed in PG18), sequence values on the logical path (fix reverted 2022), replication slots (fixed in PG17), and the validity of text indexes when glibc moves (tracking reverted 2021).
- No incident in the reachable record blames the cutover itself; the five production incidents live in the scaffolding around it: a wrong-cluster shutdown, a leftover node tripping split-brain alerts, an engine bug under real load, a broken not-yet-live cluster, and a slot that retained ~4 TB of WAL.
- Six of GitLab's upgrade attempts across three annual campaigns ended in change::aborted, each costing a reschedule rather than an outage; a mature programme treats the attempt, with written abort scenarios, as the unit of planning.
- The in-place family (Spilo, omnibus, Supabase) buys simplicity with downtime and gets sequences for free; the parallel-cluster family (GitLab.com) buys near-zero downtime and a rollback window with doubled infrastructure, a DDL freeze and the full repair list.
- The HA layer will not own this change: Patroni documents 'stop Patroni' as step one, which is why Zalando maintains a 700-line upgrade script above it and CockroachDB designed the problem away with cluster-version gates.
Scope
Why this, now. PostgreSQL 14 reaches end of life on 2026-11-12, and the engine has just spent three releases absorbing the field's upgrade workarounds (slots in 17, statistics in 18, sequences reverted and being redone), so the method is changing while the deadline is fixed.
What it does not cover. Minor-version patching, MySQL fleet upgrades, heterogeneous engine migrations, and managed-cloud extended-support pricing; blog-tier accounts (Retool, Knock, GitHub, RevenueCat) were located but unreachable from this session's network and are deliberately not cited.
Other field guides
Correctness, then cost, then neighbours: ten years of TiDB, read from its own design record
PingCAP has published a dated design document or RFC for nearly every substantial change to TiDB and TiKV since 2018, plus a release note for every v…
26 sources · 5 organisations · 4 postmortemsThe shim outlives the migration: ten years of Sentry, read from its own deletions
Every team that outgrows its first database plans the new one; almost nobody plans the period when both are running. This guide measures that period …
31 sources · 3 organisations · 6 postmortemsThere is no neutral identifier
Reads the repositories of GitLab, Rails, PostgreSQL, Twitter, Mastodon, Nextcloud, CockroachDB and the IETF's UUID revision to reconstruct how identi…
24 sources · 15 organisations · 4 postmortems