How to use this checklist
Immediately after the upgrade, before the window closes and before anybody deletes anything.
Half of it compares against measurements taken beforehand. If those were not captured, most of this checklist cannot be completed — which is itself the finding.
The backup that does not exist yet
What to compare, and against what
| Check | Compared against |
|---|---|
| Row counts on significant tables | Values captured before the window |
relkind object counts | Values captured before the window |
| Extensions and versions, every database | The pre-upgrade enumeration |
EXPLAIN output for significant queries | Plans captured before the window |
| Non-default settings | pg_settings from the old cluster |
Check what statistics arrived before rebuilding them
Things that live outside the data directory
An upgrade moves the data. It does not necessarily move:
pg_hba.confandpg_ident.conf- TLS certificate material
postgresql.conf, if it is managed separately- Anything a configuration-management tool owns
So the authentication test matters, and it must be run from the
application’s network path — a test over loopback will pass against a
trust rule regardless of whether the real path works.
Attempt a wrong credential too. A path that has only ever succeeded has not been shown capable of refusing.
Do not run delete_old_cluster.sh yet
pg_upgrade generates it, and in copy mode it is the only thing standing
between you and a rollback that costs minutes rather than a full restore.
Keep the old data directory until the new cluster has served for an agreed period, and make its removal a separate, authorised step with a note.
Record the timings
Check, upgrade, statistics, verification, total. The next upgrade is planned from these numbers.
Measured in one estate: pg_upgrade itself took 1587 ms on a small
database in copy mode. The window was sized at two hours. Knowing which
phase actually consumes the time is what makes the next window honest.
Where the numbers come from
Every figure is a comparison against the pre-upgrade capture: row
counts, object counts by relkind, extension versions in every
database, non-default settings diffed from the old cluster, and plans
for the significant queries.
What the statistics actually carried across is measured before any
ANALYZE — pg_stats and pg_class.reltuples — and recorded, because
that measurement is what tells you whether the next window needs the
statistics phase at all.
The phase timings come from the window that just ran, recorded while they are still known.
Access this needs
A role holding pg_monitor and pg_read_all_settings on every node of
the upgraded estate, plus the captured pre-upgrade state.
A non-privileged connection from the application’s own network path
with the real credential and driver, and a deliberately wrong one —
pg_hba.conf and TLS material live outside the data directory and do
not always follow an upgrade.
Write access to wherever the new physical backup will be taken, because that item is the most urgent one here and cannot be deferred to a later review.
What the review produces
A dated record naming the reviewer, the versions before and after, and the disposition of every item, with the full before-and-after comparison attached and the phase timings recorded for the next upgrade.
Two items are reported regardless of everything else: whether a new physical backup of the upgraded cluster exists, and whether the old cluster is still retained under an agreed date and owner. Until the first is true the estate’s recovery path is a restore of the old version followed by another upgrade, which is a recovery time objective nobody agreed to.
Sign-off
- Reviewer: ________________ Date: ___________
- Database owner: ___________ Date: ___________
- Service owner: ____________ Date: ___________
Every critical item must pass. A failing critical item is a blocker, not a note for the next sprint: record the date, the reviewer, the disposition of every item that did not pass, and the name of whoever accepted the residual risk.