PostGIS
PostGIS is a very popular open source extension for PostgreSQL that introduces support for storing GIS (Geographic Information Systems) objects in the database and be queried via SQL.
This section assumes you are familiar with PostGIS and provides some basic information about how to create a new PostgreSQL cluster with a PostGIS database in Kubernetes via CloudNativePG.
Supported Installation Methods
Depending on your environment (PostgreSQL version, Kubernetes version, and CloudNativePG version), you have two primary ways to provision PostGIS:
-
Image Volume Extensions: Mounts extension binaries directly from OCI images as read-only volumes (recommended for PG 18+ and Kubernetes 1.35+).
-
PostGIS Operand Images: Uses a pre-built PostgreSQL image that already contains PostGIS libraries.
Once PostGIS is provisioned on the system using one of the methods below, you must still enable and manage it inside the database (see the "Enabling the Extension" section below).
Method 1: Image Volume Extensions
Starting with PostgreSQL 18 and Kubernetes 1.35 (1.33 and 1.34 with the
ImageVolume feature gate enabled), you can leverage
image volume extensions.
This is the modern way to manage extensions, as it allows you to use an official minimal PostgreSQL image and "plug in" PostGIS at runtime, by mounting the content of the extension's OCI image directly into the Postgres container as a read-only volume. This decouples the extension lifecycle from the base PostgreSQL image, keeping your operand images slim and secure.
The CloudNativePG Community distributes standalone extension container images
for PostGIS via the
postgres-extensions-containers
repository.
The following illustrative example shows how to add PostGIS 3.6.2 to a
PostgreSQL 18 cluster, by loading the extension directly in the Cluster
resource:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-postgis
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:18.1-minimal-trixie
instances: 1
storage:
size: 1Gi
postgresql:
extensions:
- name: postgis
image:
reference: ghcr.io/cloudnative-pg/postgis-extension:3.6.2-18-trixie
ld_library_path:
- system
Refer to the postgres-extensions-containers
project for the latest versions and instructions for this extension.
Method 2: PostGIS Operand Images
For users who prefer to keep using the dedicated postgis-containers registry,
or for those on older versions of PostgreSQL or Kubernetes that do not support
image volume extensions, the community continues to maintain images with
PostGIS pre-installed (built on top of the PostgreSQL Container images).
For more information, please visit:
To use this method, point the .spec.imageName to the desired PostGIS-enabled
image:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-postgis
spec:
instances: 1
imageName: ghcr.io/cloudnative-pg/postgis:18.1-3.6.2-system-trixie
storage:
size: 1Gi
Enabling the Extension
Once the binaries are provisioned (via either method above), you must enable the extension in the database.
Using the Database resource allows for declarative management of the
extension's lifecycle, including version upgrades.
For more details, see the "Managing Extensions in a Database" section.
apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
name: cluster-postgis-app
spec:
name: app
owner: app
cluster:
name: cluster-postgis
extensions:
- name: postgis
version: '3.6.2'
- name: postgis_raster
- name: postgis_sfcgal
- name: fuzzystrmatch
- name: address_standardizer
- name: address_standardizer_data_us
- name: postgis_tiger_geocoder
- name: postgis_topology
Specifying the version in the extensions stanza is highly recommended.
CloudNativePG will compare this version with the one currently installed in the
database; if they differ, it will automatically execute the necessary
ALTER EXTENSION ... UPDATE TO ... command.
Verification
You can easily verify the available version of PostGIS that is in the
container, by connecting to the app database and querying the PostgreSQL
catalog. For example:
kubectl cnpg psql cluster-postgis -- app -c \
"SELECT * FROM pg_available_extensions WHERE name ~ '^postgis' ORDER BY 1"
Returning something similar to this (you might obtain different values from the ones in this document):
name | default_version | installed_version | comment
--------------------------+-----------------+-------------------+------------------------------------------------------------
postgis | 3.6.2 | 3.6.2 | PostGIS geometry and geography spatial types and functions
postgis-3 | 3.6.2 | | PostGIS geometry and geography spatial types and functions
postgis_raster | 3.6.2 | 3.6.2 | PostGIS raster types and functions
postgis_raster-3 | 3.6.2 | | PostGIS raster types and functions
postgis_sfcgal | 3.6.2 | 3.6.2 | PostGIS SFCGAL functions
postgis_sfcgal-3 | 3.6.2 | | PostGIS SFCGAL functions
postgis_tiger_geocoder | 3.6.2 | 3.6.2 | PostGIS tiger geocoder and reverse geocoder
postgis_tiger_geocoder-3 | 3.6.2 | | PostGIS tiger geocoder and reverse geocoder
postgis_topology | 3.6.2 | 3.6.2 | PostGIS topology spatial types and functions
postgis_topology-3 | 3.6.2 | | PostGIS topology spatial types and functions
(10 rows)
The next step is to verify that the extensions listed in the Database
resource have been correctly installed in the app database:
kubectl cnpg psql cluster-postgis -- app -c '\dx'
The command returns something like this:
List of installed extensions
Name | Version | Default version | Schema | Description
------------------------------+---------+-----------------+------------+------------------------------------------------
address_standardizer | 3.6.2 | 3.6.2 | public | Used to parse an address into constituent ...
address_standardizer_data_us | 3.6.2 | 3.6.2 | public | Address Standardizer US dataset example
fuzzystrmatch | 1.2 | 1.2 | public | determine similarities and distance between...
plpgsql | 1.0 | 1.0 | pg_catalog | PL/pgSQL procedural language
postgis | 3.6.2 | 3.6.2 | public | PostGIS geometry and geography spatial type...
postgis_raster | 3.6.2 | 3.6.2 | public | PostGIS raster types and functions
postgis_sfcgal | 3.6.2 | 3.6.2 | public | PostGIS SFCGAL functions
postgis_tiger_geocoder | 3.6.2 | 3.6.2 | tiger | PostGIS tiger geocoder and reverse geocoder
postgis_topology | 3.6.2 | 3.6.2 | topology | PostGIS topology spatial types and functions
(9 rows)
Finally:
kubectl cnpg psql cluster-postgis -- app -c 'SELECT postgis_full_version()'
Returning:
postgis_full_version
------------------------------------------------------------------------------------------------------------------------
POSTGIS="3.6.2 08d9b9f" [EXTENSION] PGSQL="180" GEOS="3.13.1-CAPI-1.19.2"
SFCGAL="SFCGAL 2.0.0, CGAL 6.0, BOOST 1.83.0"
PROJ="9.6.0 NETWORK_ENABLED=OFF URL_ENDPOINT= USER_WRITABLE_DIRECTORY=/tmp/proj"
(compiled against PROJ 9.6.0)
GDAL="GDAL 3.10.3, released 2025/04/01 GDAL_DATA not found"
LIBXML="2.9.14" LIBJSON="0.18" LIBPROTOBUF="1.5.1" WAGYU="0.5.0 (Internal)"
TOPOLOGY RASTER
(1 row)