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.
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 Catalog with 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.
Via an image catalog (for CloudNativePG 1.29+)
Starting from CloudNativePG 1.29 you can take advantage of image catalogs with image volume extensions
to automate the management of these versions.
Refer to the image volume extensions page
for instructions on how to add an extension to a PostgreSQL cluster,
making sure you use the postgis key:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-postgis
spec:
# ... <snip>
imageCatalogRef:
apiGroup: postgresql.cnpg.io
kind: ClusterImageCatalog
name: postgresql-minimal-trixie
major: 18
postgresql:
extensions:
# ... <snip>
- name: postgis
# ... <snip>
Directly in the Cluster resource
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.3-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
Note
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:
The
The
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.3-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.
Note
For more details, see the Managing Extensions in a Database .
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
Tip
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)