Home / Alt manpages / pg_upgradecluster(1)

  • pg_upgradecluster(1)
  • User command
  • linux

Upgrade a Debian PostgreSQL Cluster with pg_upgradecluster

pg_upgradecluster wraps pg_upgrade and Debian's cluster bookkeeping into one command, easy to run without knowing what it did to your ports.

You will finish with a controlled major-version upgrade: a pre-flight inventory, a chosen migration method, post-upgrade checks and a recovery path that keeps the old cluster available. The examples match postgresql-common 257build1.1, installed on this system.

Allow at least thirty minutes for a small test cluster; a production one, especially with the default dump-and-restore method, can take much longer. You need a root-capable maintenance shell, enough disk space for a second cluster or its temporary files, a tested backup and a maintenance window. This command stops or changes cluster services, so no production cluster without an agreed outage and rollback plan.

1. Inventory the cluster before changing anything

Run the inventory as an ordinary user first. pg_lsclusters shows PostgreSQL major version, cluster name, port, state, owner, data directory and log file, and two of those values become the positional arguments for the upgrade:

$ 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

Here 16 is the old version and main the cluster name. Swap in your own values, do not assume main exists, and do not mistake a PostgreSQL major version for a package revision.

Checkpoint

Record the output, confirm this is actually the cluster you mean to migrate, and make sure your backup can be restored. Check available space in both the old and new data locations too, an upgrade creates a second cluster and can fail halfway through if storage runs out.

2. Choose the destination and method

The basic form is:

sudo pg_upgradecluster -v NEW_VERSION OLD_VERSION CLUSTER_NAME

-v names the new major version. Leave it out and pg_upgradecluster picks the latest available, convenient only when that happens to be what you wanted. The destination data directory defaults to /var/lib/postgresql/NEW_VERSION/CLUSTER_NAME; pass a final directory argument when the new cluster belongs somewhere else:

sudo pg_upgradecluster -v 17 16 main /srv/postgresql/17/main

That is an illustration, not a command to paste unchanged. Check the target server and its client or common package are actually installed, using the real version your repository offers.

The default method is dump: pg_dump and pg_restore, usually the easiest to reason about but also the slowest. upgrade uses pg_upgrade directly, link is upgrade with hard links, and clone uses filesystem cloning where supported:

sudo pg_upgradecluster -v NEW_VERSION -m upgrade OLD_VERSION CLUSTER_NAME
sudo pg_upgradecluster -v NEW_VERSION -m link OLD_VERSION CLUSTER_NAME
sudo pg_upgradecluster -v NEW_VERSION -m clone OLD_VERSION CLUSTER_NAME

Hard-link and clone choices carry different storage and rollback implications. Read the installed pg_upgrade documentation and test your chosen method on a representative copy before trusting it with anything important. Do not pick link just because it sounds faster, without understanding what happens to the old data files once it runs.

3. Check configuration and extension hooks

pg_upgradecluster copies the old configuration into the new cluster and adjusts it for the new version, inheriting the old locale unless you pass --locale or one of the individual locale options. A locale change affects data and index compatibility, it is not a cosmetic setting, so only touch it once you have tested the consequences.

Extensions can need work before or after the data migration. pg_upgradecluster runs executable hook scripts from /etc/postgresql-common/pg_upgradecluster.d/, named with letters, digits, underscores and hyphens but never dots. Each one receives:

OLD_VERSION CLUSTER_NAME NEW_VERSION PHASE

The init phase gets a new, empty cluster containing only template1 and postgres. The finish phase runs once the data has been copied or restored, while the old cluster is stopped. A failing hook aborts the whole upgrade, and hooks run as the database owner, so review these scripts before you schedule anything, especially on a host running PostGIS or another extension with its own metadata.

Do not create application tables in an init hook. The dump, restore or pg_upgrade step will simply overwrite that work.

4. Run the upgrade during the maintenance window

Stop application writers, confirm clients have drained, and take a final backup the normal way. Then run the chosen command with elevated privileges:

sudo pg_upgradecluster -v NEW_VERSION OLD_VERSION CLUSTER_NAME

By default, the new cluster inherits the old cluster's original port. The old cluster gets moved to a previously unused port and switched to manual startup, a deliberate safety boundary: the old data stays available for checks, but it will not silently start again at boot.

Warning

This command changes ports, cluster state and service availability. Do not reach for --keep-port casually, it disables the normal port swap, so clients may keep hitting the old cluster or fail to find the new one unless you already have a separate connection plan sorted.

By default the new cluster starts automatically if the old one was running, or if upgrade hooks are present. Use --no-start when your deployment needs an explicit review first:

sudo pg_upgradecluster -v NEW_VERSION --no-start OLD_VERSION CLUSTER_NAME

A failed upgrade normally gets its half-built target removed automatically. Only add --keep-on-error if you actually need that failed cluster for investigation and have the space to spare, and treat whatever you keep as temporary diagnostic state, not a usable database.

5. Verify the new cluster and application connections

Start with the inventory again, read-only, no root needed:

$ pg_lsclusters
Ver Cluster Port Status Owner    Data directory              Log file
16  main    5433 manual postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log
17  main    5432 online  postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log

Exact port and status depend on your host. Confirm the new version is online, the intended port is serving it, and the old version is sitting on its reassigned port or marked manual. Then connect explicitly to the new port rather than trusting a remembered default:

$ psql --host=127.0.0.1 --port=5432 --username=APP_USER --dbname=APP_DATABASE
APP_DATABASE=> SELECT version();
APP_DATABASE=> SELECT current_database(), current_user;
APP_DATABASE=> \q

Run your application's read and write smoke tests, check the new cluster's log, verify extension versions, and confirm scheduled jobs, connection pools, replication and monitoring all still make sense. A clean pg_upgradecluster exit only means the migration command finished; it says nothing about whether every client or extension is actually ready for production.

6. Keep or remove the old cluster deliberately

Keep the old cluster until the new one has passed its application checks and your recovery plan's retention period. Need to look at the old data? Use the port pg_lsclusters shows and start it explicitly as root or the database owner:

sudo pg_ctlcluster OLD_VERSION CLUSTER_NAME start
pg_lsclusters

Never start both clusters on the same port. If the upgrade needs to be abandoned entirely, stop the new cluster, keep the old one, and restore whatever connection configuration pointed clients at the old port; the exact service, pool and application changes are yours to record before the window even starts.

Only once the new cluster is accepted, and your backup or retention policy allows it, should you remove the old one:

sudo pg_dropcluster OLD_VERSION CLUSTER_NAME

Warning

This is destructive. It deletes the old cluster's data and configuration, so check pg_lsclusters and the version and name twice before you run it. There is no useful undo for a dropped cluster; whatever happens next depends entirely on your backup.

Done means

  • Old version, cluster name, port, data directory and log recorded before touching anything.
  • Tested backup and a maintenance or rollback plan in place before the upgrade started.
  • dump, upgrade, link or clone chosen deliberately, not by default alone.
  • Extension hooks and locale changes reviewed before the run.
  • pg_lsclusters shows the new cluster online and the old cluster safely retained or removed.
  • Application connections, extensions, jobs, replication and monitoring checked against the new server.