--- title: pgd node upgrade navTitle: Upgrade deepToC: true redirects: - /pgd/latest/cli/command_ref/node/upgrade/ #generated for DOCS-1247-PGD-6.0-Docs --- ## Synopsis The `pgd node upgrade` command is used to upgrade the Postgres version on a node in the EDB Postgres Distributed cluster. ## Modes of operation `pgd node upgrade` runs in one of three modes, depending on whether `--prepare` or `--check` is set. `--prepare` and `--check` can't be used together. | Mode | Flag | Effect | |------|------|--------| | Prepare only | `--prepare` | Runs `initdb` on the new data directory and migrates the old cluster's configuration into it, without running `pg_upgrade`. Idempotent, so it's safe to re-run, for example to change a configuration override. | | Dry run | `--check` | Runs `pg_upgrade` in dry-run mode to validate that the upgrade would succeed, without performing it. Requires the new data directory to already be initialized, either manually (for example with `initdb`) or using `--prepare`. | | Full upgrade | Neither flag | Runs the [safety checks](#safety-checks-before-upgrading), then `pg_upgrade` against the new data directory, followed by the BDR post-upgrade steps. | For a predictable, low-downtime upgrade, run all three in sequence: prepare the new data directory ahead of the maintenance window, dry-run the upgrade to confirm it will succeed, then perform the actual upgrade. See [Preparing the new data directory ahead of the upgrade](#preparing-the-new-data-directory-ahead-of-the-upgrade) for a worked example. !!!note Starting from PGD 6.4, `pgd node setup` advances the `NextOID` counter before creating BDR objects in single-user mode, preventing those objects from receiving system-range OIDs (< 16384) that would cause upgrade failures. Before performing the upgrade, the command checks whether existing BDR objects were created with system-range OIDs. If detected, the command fails immediately with a clear error identifying the affected objects. See [Resolving system-range OID errors](#resolving-system-range-oid-errors) for steps to recover. !!! ### Users and roles Requires Postgres superuser privileges and the `bdr_superuser` role. See [User roles](https://www.enterprisedb.com/pgd/latest/reference/cli/command_ref/#user-roles). ## Syntax ```plaintext pgd node upgrade [OPTIONS] --old-bindir --new-bindir --old-datadir --new-datadir --database --username ``` Where `` is the name of the node which you want to upgrade and ``, ``, ``, ``, ``, and `` are the old and new Postgres instance bin directories, old and new Postgres instance data directories, database name, and cluster's install user name respectively. ## Options The following table lists the options available for the `pgd node upgrade` command: | Short | Long | Default | Env | Description | |-------|----------------------|---------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| | -b | --old-bindir | | PGBINOLD | Old Postgres instance bin directory | | -B | --new-bindir | | PGBINNEW | New Postgres instance bin directory | | -d | --old-datadir | | PGDATAOLD | Old Postgres instance data directory | | -D | --new-datadir | | PGDATANEW | New Postgres instance data directory | | | --database | | PGDATABASE | PGD database name | | -p | --old-port | Read from the old cluster's `postgresql.conf`; falls back to 5432 only if that lookup fails | PGPORTOLD | Old Postgres instance port | | | --socketdir | /var/run/postgresql | PGSOCKETDIR | Directory to use for postmaster sockets during upgrade, for both the old and new instance | | | --check | | | Run in dry-run mode, validating that the upgrade would succeed without performing it. Requires the new data directory to already be initialized, either manually (for example with `initdb`) or using `--prepare`. Can't be used with `--prepare`. | | | --prepare | | | Only perform pre-upgrade preparation (`initdb` and configuration migration) on the new data directory, without running the upgrade. Can't be used with `--check`. | | -j | --jobs | 1 | | Number of simultaneous processes or threads to use | | -k | --link | | | Use hard links instead of copying files to the new cluster | | | --old-options | | | Option to pass to old postgres command, multiple invocations are appended | | | --new-options | | | Option to pass to new postgres command, multiple invocations are appended | | -N | --no-sync | | | Don't wait for all files in the upgraded cluster to be written to disk | | -P | --new-port | 5432 | PGPORTNEW | New Postgres instance port number | | -r | --retain | | | Retain SQL and log files even after successful completion | | -U | --username | | PGUSER | Cluster's install user name | | | --clone | | | Use efficient file cloning | | | --copy-by-block | | | Used to migrate data between clusters with different encryption settings. This option is supported for databases that use Transparent Data Encryption (TDE) | | -y | --data-encryption | | | Enable Transparent Data Encryption (TDE) on the new data directory. Only takes effect with `--prepare`. | | | --data-encryption-keylen | 128 | | AES key length for TDE, either `128` or `256`. Requires `--data-encryption`. | | | --key-wrap-command | | PGDATAKEYWRAPCMD | Command to wrap (encrypt) the data encryption key. The command must contain the placeholder `%p`. Requires `--data-encryption`. Can't be used with `--no-key-wrap`. | | | --key-unwrap-command | | PGDATAKEYUNWRAPCMD | Command to unwrap (decrypt) the data encryption key and access the files to copy. The command must be the same specified during the server initialization using `pgd node setup` | | | --no-key-wrap | | | Store the data encryption key unwrapped (raw) on disk. Not recommended for production use. Requires `--data-encryption`. | | | --copy-key-from | | | Reuse an existing data encryption key file instead of generating a new one, preserving the source cluster's key material. Requires `--data-encryption`, paired with either `--key-wrap-command` or `--no-key-wrap` to describe how the reused key is encoded on disk. | | | --postgresql-conf | | | Path of the `postgresql.conf` file to use for the new node. Only takes effect with `--prepare`. | | | --postgresql-auto-conf | | | Path of the `postgresql.auto.conf` file to use for the new node. Only takes effect with `--prepare`. | | | --hba-conf | | | Path of the `pg_hba.conf` file to use for the new node. Only takes effect with `--prepare`. | | -v | --verbose | | | Print extra diagnostic output during `--prepare` steps. | See also [Global Options](https://www.enterprisedb.com/pgd/latest/reference/cli/command_ref/#global-options). After a successful upgrade, the command prints a closing summary confirming completion, along with next steps for starting the new Postgres instance (see [`pgd node start`](https://www.enterprisedb.com/docs/pgd/latest/reference/cli/command_ref/node/start)) and any remaining post-upgrade tasks. ## Safety checks before upgrading Before invoking `pg_upgrade`, the regular upgrade and `--check` paths run several non-destructive checks: - **Leader status.** If the node being upgraded is the current write leader or Raft leader of any group, the command rejects the upgrade and prints the switchover command to run first, for example [`pgd group set-leader`](https://www.enterprisedb.com/pgd/latest/reference/cli/command_ref/group/set-leader) or [`pgd raft set-leader`](https://www.enterprisedb.com/pgd/latest/reference/cli/command_ref/raft/set-leader), for each group the node leads. There's no flag to skip this check, since switching leadership away from the node being upgraded is a deliberate step, not something to automate around. - **Old cluster reachability.** If the old cluster is reachable, the command uses it to detect encoding and configuration paths. If it's not reachable, for example because it's already stopped ahead of `pg_upgrade`, the command falls back to single-user mode against the old data directory instead of requiring the old postmaster to be running. In this case, the leader-status check is skipped rather than failing, since PGD has already re-elected a leader among the surviving nodes. - **New data directory initialized.** If `` is missing or empty, the command fails with a message pointing at `--prepare` instead of a raw `pg_upgrade` error. - **BDR extension and version compatibility.** The upgrade is rejected if the BDR extension isn't installed in `` on the old cluster, or if the old cluster's BDR version is below the supported minimum or higher than the new cluster's. ## Resolving system-range OID errors If the command fails with a system-range OID error, the cluster was set up with a version of `pgd node setup` earlier than 6.4 that didn't advance the `NextOID` counter before creating BDR objects in single-user mode. The affected objects can't be reassigned new OIDs in place. To resolve the error, part the node, drop and recreate the BDR extension in a standard psql session (not single-user mode), then rejoin the cluster without synchronizing structure to preserve existing data. 1. Part the node from the cluster: ```shell pgd node part ``` 2. In a psql session connected to the BDR database, drop the extension: ```sql DROP EXTENSION bdr CASCADE; ``` 3. In a standard psql session (not single-user mode), recreate the extension: ```sql CREATE EXTENSION bdr; ``` 4. Use [`bdr.create_node()`](https://www.enterprisedb.com/pgd/latest/reference/tables-views-functions/nodes-management-interfaces#bdrcreate_node) and [`bdr.join_node_group()`](https://www.enterprisedb.com/pgd/latest/reference/tables-views-functions/nodes-management-interfaces#bdrjoin_node_group) to recreate the node and rejoin the group, with `synchronize_structure` set to `'none'`. 5. Run `pgd node upgrade` again. ## Examples In the following examples, "kaolin" is the name of the node to upgrade, from the Quickstart democluster. ### Upgrade the Postgres version on a node ```shell pgd node kaolin upgrade --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb ``` ### Upgrade the Postgres version on a node with hard links ```shell pgd node kaolin upgrade --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb --link ``` ### Upgrade the Postgres version on a node with efficient file cloning ```shell pgd node kaolin upgrade --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb --clone ``` ### Upgrade the Postgres version on a node with a different port number ```shell pgd node kaolin upgrade --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb --old-port 5433 --new-port 5434 ``` ### Preparing the new data directory ahead of the upgrade Use `--prepare` to run `initdb` and migrate the old cluster's configuration into the new data directory, without running `pg_upgrade`: ```shell pgd node kaolin upgrade --prepare --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb ``` `--prepare` is idempotent, so re-running the same command later skips files that are already in place and only updates what's changed. This lets you prepare the new data directory well ahead of a maintenance window, then perform the actual upgrade later with minimal downtime. ### Validating a prepared upgrade before running it Once the new data directory is prepared, run the same command with `--check` instead of `--prepare` to validate that the upgrade would succeed, without changing any data: ```shell pgd node kaolin upgrade --check --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb ``` If the check succeeds, run the same command again with neither `--prepare` nor `--check` to perform the upgrade against the prepared data directory. ### Preparing the new data directory with custom configuration files The `--postgresql-conf`, `--postgresql-auto-conf`, and `--hba-conf` overrides apply only with `--prepare`, since the regular upgrade and `--check` paths use the new data directory's existing configuration as is: ```shell pgd node kaolin upgrade --prepare --old-bindir /usr/pgsql-16/bin --new-bindir /usr/pgsql-17/bin --old-datadir /var/lib/pgsql/16/data --new-datadir /var/lib/pgsql/17/data --database pgddb --username enterprisedb --postgresql-conf /opt/new-configs/postgresql.conf --hba-conf /opt/new-configs/pg_hba.conf ``` To change these overrides after the fact, re-run `--prepare` with the new paths. It's safe to do this any number of times before the actual upgrade runs. ### Upgrade the Postgres Extended version on a node with Transparent Data Encryption (TDE) `--data-encryption` and its related flags only take effect during `--prepare`, which provisions TDE on the new data directory. `--key-unwrap-command` and `--copy-by-block` are passed again on the actual upgrade, since that's the step that decrypts the old data to copy it block by block into the newly encrypted data directory. ```shell pgd node kaolin upgrade --prepare --database pgddb -B /usr/lib/edb-pge/16/bin --socketdir /var/run/edb-pge/ --old-bindir /usr/lib/edb-pge/15/bin --old-datadir /var/lib/edb-pge/15/main --new-datadir /var/lib/edb-pge/16/main --username postgres --data-encryption --key-wrap-command "openssl enc -aes-128-cbc -pbkdf2 -pass pass:secret -out %p" --key-unwrap-command "openssl enc -d -aes-128-cbc -pbkdf2 -pass pass:secret -in %p" pgd node kaolin upgrade --database pgddb -B /usr/lib/edb-pge/16/bin --socketdir /var/run/edb-pge/ --old-bindir /usr/lib/edb-pge/15/bin --old-datadir /var/lib/edb-pge/15/main --new-datadir /var/lib/edb-pge/16/main --username postgres --key-unwrap-command "openssl enc -d -aes-128-cbc -pbkdf2 -pass pass:secret -in %p" --copy-by-block ``` To preserve the old cluster's existing encryption key instead of generating a new one, add `--copy-key-from /pg_encryption/key.bin` to the `--prepare` step, alongside `--key-wrap-command` or `--no-key-wrap`. > For an AI-friendly overview of our documentation, see [llms.txt](https://www.enterprisedb.com/docs/llms.txt).