Upgrading PostgreSQL keeps security fixes and performance improvements flowing while preventing the quiet risk of running an out-of-support database. A safe upgrade approach reduces downtime surprises, preserves data integrity, and keeps a rollback option available until applications are confirmed healthy.
Minor updates within the same PostgreSQL major version are typically in-place package upgrades that keep the on-disk format compatible. Major version upgrades change internal catalogs and sometimes on-disk structures, so the server binaries must be upgraded with tooling such as pg_upgrade, which migrates a cluster to the new major version without rebuilding every database from scratch.
Package-based installations on Debian or Ubuntu can keep old and new major versions installed side-by-side as separate clusters under /var/lib/postgresql, managed by postgresql-common tools such as pg_lsclusters and pg_upgradecluster. Major upgrades require a maintenance window, a verified backup stored outside the data directory, enough free disk space for copy-based upgrades, and matching extension packages for the target major version.
Related: How to create a PostgreSQL database backup \\
Related: How to restore a PostgreSQL database backup
Steps to upgrade PostgreSQL using pg_upgradecluster:
- Schedule a maintenance window for database downtime.
- List existing PostgreSQL clusters and ports.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 15 main 5432 online postgres /var/lib/postgresql/15/main /var/log/postgresql/postgresql-15-main.log
- Note the source cluster Ver plus Cluster values for the upgrade (example: 15 / main).
- Check free disk space for the filesystem hosting /var/lib/postgresql.
$ df -h /var/lib/postgresql Filesystem Size Used Avail Use% Mounted on overlay 1.8T 28G 1.7T 2% /
pg_upgradecluster copy mode requires free space close to the size of the source cluster, because a new data directory is created for the target major version.
- Create a backup directory owned by postgres.
$ sudo install --directory --owner=postgres --group=postgres --mode=0700 /var/backups/postgresql
No output indicates the directory was created successfully.
- Create a full logical backup of all databases and global objects.
$ sudo -u postgres pg_dumpall --file=/var/backups/postgresql/pg_dumpall-2026-01-01.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.
- Restrict the backup file permissions.
$ sudo chmod 0600 /var/backups/postgresql/pg_dumpall-2026-01-01.sql
- Verify that the backup file exists and is readable only by its owner.
$ sudo ls -l /var/backups/postgresql/pg_dumpall-2026-01-01.sql -rw------- 1 postgres postgres 2058 Jan 1 08:49 /var/backups/postgresql/pg_dumpall-2026-01-01.sql
- Sanity-check the backup header to confirm a valid dump file.
$ sudo -u postgres head -n 6 /var/backups/postgresql/pg_dumpall-2026-01-01.sql -- -- PostgreSQL database cluster dump -- \restrict q4CV77rvL5DUvFpDVqcla1jpBJUv6S6QvS0OeVwmmPSv0kLUeehTxq3yucg92NL
- Update package indexes.
$ sudo apt update WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Hit:1 http://apt.postgresql.org/pub/repos/apt noble-pgdg InRelease Hit:2 http://ports.ubuntu.com/ubuntu-ports noble InRelease ##### snipped ##### Reading package lists... Building dependency tree... Reading state information... All packages are up to date.
- Install the target PostgreSQL major version packages (example: 16).
$ sudo apt install --assume-yes postgresql-16 postgresql-client-16 WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Reading package lists... Building dependency tree... Reading state information... Suggested packages: postgresql-doc-16 The following NEW packages will be installed: postgresql-16 postgresql-client-16 ##### snipped ##### Setting up postgresql-16 (16.11-1.pgdg24.04+1) ... ##### snipped #####
- Confirm the source cluster is online before the upgrade.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 15 main 5432 online postgres /var/lib/postgresql/15/main /var/log/postgresql/postgresql-15-main.log
pg_upgradecluster creates the target cluster during the upgrade, so it is normal to see only the source cluster here.
- Stop the source cluster.
$ sudo pg_ctlcluster 15 main stop
- Run the major version upgrade in copy mode.
$ sudo pg_upgradecluster --method=upgrade 15 main Upgrading cluster 15/main to 16/main ... Restarting old cluster with restricted connections... Stopping old cluster... Creating new PostgreSQL cluster 16/main ... ##### snipped ##### Success. Please check that the upgraded cluster works. If it does, you can remove the old cluster with pg_dropcluster 15 mainEnsure extension packages for the target major version are installed before upgrading (example: postgresql-16-<extension>), because pg_upgrade requires matching extension binaries.
- List clusters to confirm the target version is online on the original port.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 15 main 5433 down postgres /var/lib/postgresql/15/main /var/log/postgresql/postgresql-15-main.log 16 main 5432 online postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log
The source cluster is commonly kept in down state on a different port for rollback until it is removed.
- Check that PostgreSQL accepts connections.
$ sudo -u postgres pg_isready /var/run/postgresql:5432 - accepting connections
- Verify the upgraded server version.
$ sudo -u postgres psql -c "SELECT version();" version ------------------------------------------------------------------------------------------------------------------------------------------- PostgreSQL 16.11 (Ubuntu 16.11-1.pgdg24.04+1) on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit (1 row) - Rebuild planner statistics for all databases.
$ sudo -u postgres vacuumdb --all --analyze-in-stages --verbose vacuumdb: processing database "postgres": Generating minimal optimizer statistics (1 target) ##### snipped ##### vacuumdb: processing database "template1": Generating minimal optimizer statistics (1 target) ##### snipped #####
Refreshing statistics helps the query planner make good choices immediately after a major upgrade.
- Check the upgraded cluster service status for active (running) state.
$ sudo systemctl status postgresql@16-main ● postgresql@16-main.service - PostgreSQL Cluster 16-main Loaded: loaded (/usr/lib/systemd/system/postgresql@.service; enabled-runtime; preset: enabled) Active: active (running) since Thu 2026-01-01 08:49:17 UTC; 941ms ago ##### snipped ##### - Remove the old cluster when rollback is no longer needed.
$ sudo pg_dropcluster --stop 15 main
pg_dropcluster permanently deletes the cluster data directory, so application verification must be complete before removal.
- Purge old major version packages when no longer needed.
$ sudo apt purge --assume-yes postgresql-15 postgresql-client-15 WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Reading package lists... Building dependency tree... Reading state information... The following packages will be REMOVED: postgresql-15* postgresql-client-15* 0 upgraded, 0 newly installed, 2 to remove and 0 not upgraded. After this operation, 64.4 MB disk space will be freed. ##### snipped #####
- Remove orphaned dependencies.
$ sudo apt autoremove --assume-yes WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Reading package lists... Building dependency tree... Reading state information... 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
- Confirm that only the target cluster remains.
$ sudo pg_lsclusters Ver Cluster Port Status Owner Data directory Log file 16 main 5432 online postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
