Skip to main content
RunBook Academy

PostgreSQLXVII · Capacity, Maintenance and UpgradesMaintenance

Major version upgrade options compared

Intermediate⏱ ~30 min

What you'll learn

  • Compare dump/restore, pg_upgrade and logical replication honestly
  • Choose a route from downtime tolerance and cluster size
  • Identify what each route does not carry across
  • Plan the rollback for each

Prerequisites

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

Major versions change the on-disk format, so the data must be moved. There are four ways, and the right one is decided almost entirely by downtime tolerance and size.

The four routes

RouteDowntimeCluster sizeRollback
pg_dump / pg_restoreHours to daysSmall onlyOld cluster untouched
pg_upgrade --copy (default)Minutes to hoursMediumOld cluster untouched
pg_upgrade --link / --swapMinutesAnyNone. See below
Logical replicationSecondsAnySwitch back

Dump and restore

Simple, thorough, and slow. Everything is read through the SQL layer and every index rebuilt, which lesson XIII-02 covered.

It has one property nothing else offers: it produces a cluster with no inherited physical baggage — no bloat, no fragmented indexes. That is occasionally worth the downtime on a small database and never worth it on a large one.

pg_upgrade in copy mode

The default. Copies the data files, converts the catalogue.

Read-only / Safea real 17.11 to 18.6 upgrade, copy mode
$ pg_upgrade --old-datadir=... --new-datadir=... --old-bindir=... --new-bindir=...
Upgrade Complete
----------------
ELAPSED: 2 seconds

--link hard-links the data files into the new cluster instead of copying them, so the time is independent of cluster size — minutes on any size.

Read-only / Safethe transfer modes, compared between the two binaries
$ /usr/lib/postgresql/17/bin/pg_upgrade --help | grep -E 'link|clone|copy|swap'
/usr/lib/postgresql/18/bin/pg_upgrade --help | grep -E 'link|clone|copy|swap'
--- 17 ---
-k, --link                    link instead of copying files to new cluster
    --clone                   clone instead of copying files to new cluster
    --copy                    copy files to new cluster (default)
    --copy-file-range         copy files to new cluster with copy_file_range

--- 18 ---
(the same four, plus)
    --swap                    move data directories to new cluster

Logical replication

Replicate from the old major version to a new one, let it catch up, then switch the application over. Downtime is the switchover — seconds.

It is the only route that offers near-zero downtime on a large cluster, and it costs the most in preparation:

  • Every table needs a replica identity — a primary key, or an explicitly configured one. Tables without one cannot replicate updates or deletes.
  • Sequences are not replicated. They must be advanced on the target before the switchover, and forgetting produces duplicate key errors immediately afterwards.
  • DDL is not replicated. The schema must be kept in step manually.
  • Large objects are not replicated.
  • The initial sync of a large table is itself a long operation.

It is a project rather than a procedure, and on a large cluster with a strict downtime budget it is the right one.

Choosing

SituationRoute
Small database, downtime availableDump and restore
Medium, hours availablepg_upgrade --copy
Large, minutes available, backup verifiedpg_upgrade --link / --swap
Large, seconds availableLogical replication
Reflink-capable filesystempg_upgrade --clone

What none of them carry across for free

Configuration. postgresql.conf is not migrated. Settings are removed and added between major versions, and a copied file can contain settings the new version rejects at startup.

Extensions. They must be installed for the new major version before the upgrade. pg_upgrade --check reports missing ones, which is a large part of why the check is worth running days ahead.

Cumulative statistics. Lesson XVII-07 measured last_analyze NULL and n_live_tup zero after an upgrade, even in 18 where planner statistics survive.

Anything outside the cluster. Cron jobs, backup scripts, monitoring queries referencing views that changed — lesson XVI-03’s pg_stat_bgwriter split is exactly this.

What to take from this

  • Four routes, chosen by downtime tolerance and size, not by elegance.
  • Measured: copy mode on 78 MB took 2 seconds. That establishes the mechanism and nothing about a terabyte.
  • --link and --swap have no rollback. The fallback is a restore.
  • --swap is new in 18, verified by comparing the 17 and 18 binaries.
  • Logical replication gives seconds of downtime and needs replica identities, manual sequence advancement, and schema discipline.
  • None of them carry configuration, extensions, cumulative statistics or anything outside the cluster.

Cross-course references

  • Linux for Production Sysadmins — Part LXXIII (Change management) covers choosing an approach by its rollback cost rather than by its runtime, which is the decision this lesson turns on.
  • Git, CI/CD & GitOps — Part LX (Forward fix versus rollback) covers the same choice in delivery terms, including the case where rollback stops being available partway through.

Quiz

Knowledge check · 6 questions

  1. Q1. A 4 TB cluster must be upgraded with under an hour of downtime, and a verified backup exists. Which route fits?

  2. Q2. After a pg_upgrade --link, the new cluster is running and the application reports a problem. Why can the old cluster not simply be started?

  3. Q3. A logical-replication upgrade completes and the application immediately hits duplicate key errors on insert. What was missed?

  4. Q4. Which are NOT carried across by pg_upgrade? Select all that apply.

  5. Q5. A copy-mode pg_upgrade measured at 2 seconds on a 78 MB cluster supports an estimate for a multi-terabyte upgrade.

  6. Q6. Why can pg_upgrade move terabytes of user data in minutes, and what does that imply about when it cannot?

Passing score: 75%. Answers are checked in this browser.