Scenario and prerequisites

The task is a single-pass move from PostgreSQL 13 with PostGIS 2.5.5 to PostgreSQL 15 with PostGIS 3.3.2. The demo machine is an Ubuntu 20.04.02 server hosting two clusters side by side: PostgreSQL 13 with PostGIS 2.5.5, and PostgreSQL 15 with PostGIS 3.3.2.

The work proceeds in three stages: establish the status quo, run pg_upgrade, then upgrade PostGIS at the database level.

Taking inventory

Start at the OS level by listing installed packages.

1

2

3

4

5

6

7

8

9

10

11

12

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# apt list --installed | grep postgresql

postgresql-13-postgis-2.5-scripts/focal-pgdg,now 2.5.5+dfsg-1.pgdg20.04+2 all [installed,automatic]

postgresql-13-postgis-2.5/focal-pgdg,now 2.5.5+dfsg-1.pgdg20.04+2 amd64 [installed]

postgresql-13/focal-pgdg,now 13.10-1.pgdg20.04+1 amd64 [installed]

postgresql-15-postgis-3-scripts/focal-pgdg,now 3.3.2+dfsg-1.pgdg20.04+1 all [installed,automatic]

postgresql-15-postgis-3/focal-pgdg,now 3.3.2+dfsg-1.pgdg20.04+1 amd64 [installed]

postgresql-15/focal-pgdg,now 15.2-1.pgdg20.04+1 amd64 [installed]

postgresql-client-13/focal-pgdg,now 13.10-1.pgdg20.04+1 amd64 [installed,automatic]

postgresql-client-15/focal-pgdg,now 15.2-1.pgdg20.04+1 amd64 [installed,automatic]

postgresql-client-common/focal-pgdg,now 248.pgdg20.04+1 all [installed,automatic]

postgresql-common/focal-pgdg,now 248.pgdg20.04+1 all [installed,automatic]

Next, pull the cluster states to confirm both are running.

1

2

3

4

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# pg_lsclusters

Ver Cluster Port Status Owner    Data directory              Log file

13  pg_13    5432 online postgres /var/lib/postgresql/13/main /var/log/postgresql/postgresql-13-main.log

15  pg_15    5433 online postgres /var/lib/postgresql/15/main /var/log/postgresql/postgresql-15-main.log

The legacy cluster listens on port 5432 and serves exactly one spatially enabled database, pgdemo, which uses PostGIS 2.5.5. A quick connection confirms what needs to move.

1

2

3

4

5

6

7

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:/root$ psql -p 5432

postgres=# c pgdemo

pgdemo=# select postgis_full_version();

                                                                                            postgis_full_version

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

POSTGIS='2.5.5' [EXTENSION] PGSQL='130' GEOS='3.8.0-CAPI-1.13.1 ' PROJ='Rel. 6.3.1, February 10th, 2020' GDAL='GDAL 3.0.4, released 2020/01/28' LIBXML='2.9.10' LIBJSON='0.13.1' LIBPROTOBUF='1.3.3' RASTER

(1 row)

Migrating the cluster with pg_upgrade

Rather than upgrading PostgreSQL and PostGIS separately, pg_upgrade handles both in one pass. Stop the clusters first.

1

2

3

4

5

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# systemctl stop postgresql

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# pg_lsclusters

Ver Cluster   Port Status Owner    Data directory                   Log file

13  pg_13 5432 down   postgres /var/lib/postgresql/13/pg_13 /var/log/postgresql/postgresql-13-pg_13.log

15  pg_15 5433 down   postgres /var/lib/postgresql/15/pg_15 /var/log/postgresql/postgresql-15-pg_15.log

Then, as user postgres, build the pg_upgrade command with the check parameter, which verifies upgrade compatibility before anything is actually migrated.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# su postgres

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:/root$ cd ~

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:~$ /usr/lib/postgresql/15/bin/pg_upgrade  

> --old-datadir=/var/lib/postgresql/13/pg_13

> --new-datadir=/var/lib/postgresql/15/pg_15

> --old-bindir=/usr/lib/postgresql/13/bin

> --new-bindir=/usr/lib/postgresql/15/bin

> --old-options '-c config_file=/etc/postgresql/13/pg_13/postgresql.conf'  

> --new-options '-c config_file=/etc/postgresql/15/pg_15/postgresql.conf'   --check

Performing Consistency Checks

-----------------------------

Checking cluster versions                                   ok

Checking database user is the install user                  ok

Checking database connection settings                       ok

Checking for prepared transactions                          ok

Checking for system-defined composite types in user tables  ok

Checking for reg* data types in user tables                 ok

Checking for contrib/isn with bigint-passing mismatch       ok

Checking for user-defined encoding conversions              ok

Checking for user-defined postfix operators                 ok

Checking for incompatible polymorphic functions             ok

Checking for presence of required libraries                 fatal

Your installation references loadable libraries that are missing from the

new installation.  You can add these libraries to the new installation,

or remove the functions using them from the old installation.  A list of

problem libraries is in the file:

    /var/lib/postgresql/15/pg_15/pg_upgrade_output.d/20230424T070230.213/loadable_libraries.txt

Handling the library mismatch

The check fails with a familiar line: Checking for presence of required libraries fatal. The referenced log reports PostGIS 2.5 libraries missing for PostgreSQL 15 — libraries pg_upgrade needs before it will continue.

1

2

3

4

5

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:~$ cat /var/lib/postgresql/15/pg_15/pg_upgrade_output.d/20230424T070230.213/loadable_libraries.txt

could not load library '$libdir/postgis-2.5': ERROR:  could not access file '$libdir/postgis-2.5': No such file or directory

In database: pgdemo

could not load library '$libdir/rtpostgis-2.5': ERROR:  could not access file '$libdir/rtpostgis-2.5': No such file or directory

In database: pgdemo

pg_upgrade demands that both clusters carry not only the same extensions but the same extension versions. The PostGIS compatibility matrix in the PostGIS Wiki is the place to work out the options.

Table showing features of different PostgreSQL versions
  1. Install PostGIS 2.5 for PostgreSQL 15 at the OS level, run pg_upgrade, then upgrade to PostGIS 3.3.2 at the database level. More work, but the legacy system stays untouched.
  2. Install PostGIS 3.2 for PostgreSQL 13 on both the OS and database level to remove the version mismatch — again, more work.
  3. Persuade pg_upgrade that the required 2.5 libraries are already present for PostgreSQL 15.

Option 3 is the shortcut. It is not an official upgrade procedure, so back up first. Because the pg_upgrade log spells out exactly which libraries it expects, symbolic links from PostGIS 3 to PostGIS 2.5.5 satisfy the check.

1

2

3

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# cd /usr/lib/postgresql/15/lib/

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:/usr/lib/postgresql/15/lib# ln -s postgis-3.so postgis-2.5.so

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:/usr/lib/postgresql/15/lib# ln -s postgis_raster-3.so rtpostgis-2.5.so

With that in place, the conflicting-extension complaint disappears:

1

2

3

4

5

6

7

8

9

10

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:/usr/lib/postgresql/15/lib# su postgres

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:/usr/lib/postgresql/15/lib$ cd ~

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:~$ /usr/lib/postgresql/15/bin/pg_upgrade  --old-datadir=/var/lib/postgresql/13/pg_13 --new-datadir=/var/lib/postgresql/15/pg_15 --old-bindir=/usr/lib/postgresql/13/bin --new-bindir=/usr/lib/postgresql/15/bin --old-options '-c config_file=/etc/postgresql/13/pg_13/postgresql.conf'  --new-options '-c config_file=/etc/postgresql/15/pg_15/postgresql.conf'   --check

Performing Consistency Checks

Checking for presence of required libraries                 ok

Checking database user is the install user                  ok

Checking for prepared transactions                          ok

Checking for new cluster tablespace directories             ok

*Clusters are compatible*

Now run pg_upgrade without the check parameter. An excerpt of the output:

1

2

3

4

5

6

7

8

9

10

11

12

13

Setting next OID for new cluster                            ok

Sync data directory to disk                                 ok

Creating script to delete old cluster                       ok

Checking for extension updates                              notice

Your installation contains extensions that should be updated

with the ALTER EXTENSION command.  The file

    update_extensions.sql

when executed by psql by the database superuser will update

these extensions.

Upgrade Complete

----------------

Upgrading PostGIS inside the database

Start cluster pg_15 and connect to pgdemo.

1

2

3

4

5

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# pg_ctlcluster 15 pg_15 start

root@ubuntu-s-2vcpu-2gb-intel-fra1-01:~# su postgres

postgres@ubuntu-s-2vcpu-2gb-intel-fra1-01:/root$ psql -p 5433

postgres=# c pgdemo

You are now connected to database 'pgdemo' as user 'postgres'.

The PostGIS version registered in the database is still 2.5.5.

1

2

3

4

5

6

7

pgdemo=# dx

                                  List of installed extensions

  Name   | Version |   Schema   |                             Description

---------+---------+------------+---------------------------------------------------------------------

plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language

postgis | 2.5.5   | public     | PostGIS geometry, geography, and raster spatial types and functions

(2 rows)

postgis_extensions_upgrade() resolves this — run it twice, because from PostGIS v3 onward vector and raster functionality live in two separate extensions.

1

2

3

4

5

6

7

8

9

10

11

12

pgdemo=# select postgis_extensions_upgrade();

WARNING:  unpackaging raster

WARNING:  PostGIS Raster functionality has been unpackaged

HINT:  type `SELECT postgis_extensions_upgrade();` to finish the upgrade. After upgrading, if you want to drop raster, run: DROP EXTENSION postgis_raster;

NOTICE:  ALTER EXTENSION postgis UPDATE TO '3.3.2';

pgdemo=# select postgis_extensions_upgrade();

NOTICE:  Updating extension postgis 3.3.2

NOTICE:  Packaging and updating postgis_raster

                             postgis_extensions_upgrade

------------------------------------------------------------------------------------

Upgrade to version 3.3.2 completed, run SELECT postgis_full_version(); for details

(1 row)

postgis_full_version() then confirms the result:

1

2

3

pgdemo=# select postgis_full_version();

POSTGIS='3.3.2 4975da8' [EXTENSION] PGSQL='150' GEOS='3.8.0-CAPI-1.13.1 ' PROJ='6.3.1' GDAL='GDAL 3.0.4, released 2020/01/28' LIBXML='2.9.10' LIBJSON='0.13.1' LIBPROTOBUF='1.3.3' WAGYU='0.5.0 (Internal)' RASTER

(1 row)

Outcome

Clusters holding mismatched PostGIS versions can be upgraded in one pass, which saves time and sidesteps PostGIS version conflicts during the migration. Back up the databases before trying it.