Upgrading PostgreSQL changes the database server that applications depend on, so the work needs a backup, a maintenance window, and proof that the upgraded cluster still serves the same data on the expected port. The highest-risk moment is the major-version cutover, when the old cluster stops, the new binaries take over, and rollback remains possible only while the old cluster is still kept intact.
Minor updates within the same PostgreSQL major version are package updates and restarts; they do not need a dump and restore. Major upgrades change the system catalogs and require an upgrade tool such as pg_upgrade. On Debian and Ubuntu package installations, postgresql-common wraps that tool with pg_upgradecluster so the old and new major versions can live side by side as named clusters.
A package-managed PostgreSQL 17 to PostgreSQL 18 upgrade on Ubuntu 26.04 with PGDG packages provides the example path. Use the source and target versions that match the server being upgraded, install matching target-version extension packages before the cutover, and avoid combining the database major upgrade with an operating-system or architecture migration unless that full path has already been rehearsed.
Related: How to create a PostgreSQL database backup \\
Related: How to restore a PostgreSQL database backup
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 17 main 5432 online postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log
$ df -h /var/lib/postgresql Filesystem Size Used Avail Use% Mounted on nvme0n1p2 480G 132G 326G 29% /
pg_upgradecluster –method=upgrade copies cluster files by default when it runs pg_upgrade. Plan free space close to the source cluster size unless a tested link or clone mode is intentionally used.
$ sudo install --directory --owner=postgres --group=postgres --mode=0700 /var/backups/postgresql
No output indicates the directory was created successfully.
$ sudo -u postgres pg_dumpall --file=/var/backups/postgresql/pg_dumpall-2026-06-07.sql
pg_dumpall output contains database contents plus role definitions and can include password hashes, so the backup file must be protected like a private key.
$ sudo chmod 0600 /var/backups/postgresql/pg_dumpall-2026-06-07.sql
$ sudo ls -l /var/backups/postgresql/pg_dumpall-2026-06-07.sql -rw------- 1 postgres postgres 4266 Jun 7 04:45 /var/backups/postgresql/pg_dumpall-2026-06-07.sql
A file listing is not a restore test. Test the backup on a separate host or disposable cluster before using this workflow on production data. Related: How to restore a PostgreSQL database backup
$ sudo apt-get update Hit:1 http://archive.ubuntu.com/ubuntu resolute InRelease Hit:2 http://security.ubuntu.com/ubuntu resolute-security InRelease Get:3 https://apt.postgresql.org/pub/repos/apt resolute-pgdg InRelease [181 kB] ##### snipped ##### Reading package lists... Done
$ sudo apt-get install -y postgresql-18 postgresql-client-18 Reading package lists... Building dependency tree... Reading state information... Suggested packages: postgresql-doc-18 The following NEW packages will be installed: postgresql-18 postgresql-client-18 ##### snipped ##### Setting up postgresql-18 (18.4-1.pgdg26.04+1) ... ##### snipped #####
Install matching target-version extension packages before the upgrade, such as postgresql-18-postgis-3 for a cluster that uses PostGIS.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 17 main 5432 online postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log
pg_upgradecluster creates the target cluster during the upgrade, so it is normal to see only the source cluster here.
$ sudo pg_ctlcluster 17 main stop
$ sudo pg_upgradecluster --method=upgrade 17 main
Upgrading cluster 17/main to 18/main ...
Restarting old cluster with restricted connections...
Stopping old cluster...
Creating new PostgreSQL cluster 18/main ...
##### snipped #####
Success. Please check that the upgraded cluster works. If it does,
you can remove the old cluster with
pg_dropcluster 17 main
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 17 main 5433 down postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log 18 main 5432 online postgres /var/lib/postgresql/18/main /var/log/postgresql/postgresql-18-main.log
The source cluster is kept down on another port for rollback until it is removed.
$ sudo -u postgres pg_isready /var/run/postgresql:5432 - accepting connections
$ sudo -u postgres psql -c "SELECT version();"
version
-------------------------------------------------------------------------------------------------------------------------------------------
PostgreSQL 18.4 (Ubuntu 18.4-1.pgdg26.04+1) on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0, 64-bit
(1 row)
$ sudo -u postgres psql -d appdb -c "SELECT * FROM upgrade_check;" id | note ----+---------------- 1 | before upgrade (1 row)
Use a real application table, migration marker, health query, login flow, or read-only transaction that proves the upgraded cluster is serving the expected data.
$ sudo -u postgres vacuumdb --all --analyze-in-stages --verbose vacuumdb: processing database "postgres": Generating minimal optimizer statistics (1 target) ##### snipped ##### INFO: finished analyzing table "appdb.public.upgrade_check" ##### snipped #####
Refreshing statistics helps the query planner make good choices immediately after a major upgrade.
$ sudo pg_ctlcluster 18 main status pg_ctl: server is running (PID: 7986) /usr/lib/postgresql/18/bin/postgres "-D" "/var/lib/postgresql/18/main" "-c" "config_file=/etc/postgresql/18/main/postgresql.conf"
$ sudo pg_dropcluster --stop 17 main
pg_dropcluster permanently deletes the old cluster data directory. Keep the old cluster until application verification, monitoring, backup, and rollback decisions are complete.
$ sudo apt-get purge --assume-yes postgresql-17 postgresql-client-17 Reading package lists... Building dependency tree... Reading state information... The following packages will be REMOVED: postgresql-17* postgresql-client-17* 0 upgraded, 0 newly installed, 2 to remove and 0 not upgraded. After this operation, 68.1 MB disk space will be freed. ##### snipped #####
$ sudo apt-get autoremove --assume-yes Reading package lists... Building dependency tree... Reading state information... 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 18 main 5432 online postgres /var/lib/postgresql/18/main /var/log/postgresql/postgresql-18-main.log