Image Volume Extensions
=======================
.. raw:: html
CloudNativePG supports the **dynamic loading of PostgreSQL extensions**
into a ``Cluster`` at Pod startup using the `Kubernetes `ImageVolume` feature `_
alongside the ``extension_control_path`` GUC introduced in PostgreSQL
18, a feature to which the CloudNativePG project contributed.
This feature allows you to mount a `PostgreSQL extension `_ , packaged as an
OCI-compliant container image, as a read-only and immutable volume at a
designated filesystem path within a running pod.
For extensions requiring database-level installation via the
``CREATE EXTENSION`` command, you can use the :ref:`Configure the application database `
to ensure consistent, automated setup across all your PostgreSQL
databases.
Official Extension Images and Catalogs
--------------------------------------
The CloudNativePG Community maintains a suite of extension container
images, including `pgvector `_
and `PostGIS `_ , as part of the `postgres-extensions-containers `_ ). These images are
built on top of the `official PostgreSQL `minimal` images `_ .
.. Note::
While this documentation provides the necessary technical specifications for third parties to build their own images and catalogs, the following instructions focus specifically on the deployment and usage of our official extension images and catalogs.
Benefits
--------
By decoupling the distribution of extensions from the PostgreSQL operand
images, this feature removes a significant barrier to running PostgreSQL
in containers. It eliminates the need to embed extensions at build time,
allowing you to use official minimal operand images and dynamically add
only the required extensions to your ``Cluster`` definitions—either
directly or via an :ref:`Image Catalog ` .
This approach significantly reduces the attack surface of your database
clusters by ensuring that the core database container contains only the
essential binaries required for operation. By excluding unnecessary
extensions, libraries, and build-time dependencies, you minimize
potential entry points for exploits and simplify vulnerability
management. This architecture enhances supply chain security and reduces
operational overhead by maintaining an immutable, minimal base image for
your data workloads.
.. Important::
Extension images must be built according to the :ref:`Image Specifications ` .
Requirements
------------
To use image volume extensions with CloudNativePG, you need:
- **PostgreSQL 18 or later**: Required for ``extension_control_path``
support.
- **Kubernetes 1.35 or later**: The ``ImageVolume`` feature is enabled
by default. Users on Kubernetes 1.33 and 1.34 must manually enable the
``ImageVolume`` feature gate.
- **Container runtime with ``ImageVolume`` support**:
- ``containerd`` v2.1.0 or later, or
- ``CRI-O`` v1.31 or later.
- **CloudNativePG-compatible extension container images**, ensuring:
- Matching PostgreSQL major version of the ``Cluster`` resource.
- Compatible operating system distribution of the ``Cluster``
resource.
- Matching CPU architecture of the ``Cluster`` resource.
How it works
------------
An extension image can be added to a new or existing ``Cluster``
resource using the ``.spec.postgresql.extensions`` stanza.
.. Important::
When a new extension is added to a running `Cluster` , CloudNativePG will automatically trigger a :ref:`Rolling updates ` to attach the new image volume to each pod. Before adding a new extension in production, ensure you have thoroughly tested it in a staging environment to prevent configuration issues that could leave your PostgreSQL cluster in an unhealthy state. !!! Note For field-level details, see the :ref:`API reference for `ExtensionConfiguration` ` .
Configuration Source and Precedence
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``extensions`` stanza accepts a list of entries, each requiring a
``name`` that must be unique within the cluster.
.. Important::
The `name` must consist of lowercase alphanumeric characters, underscores (`_` ) or hyphens (`-` ) and must start and end with an alphanumeric character. Each entry defines the configuration for a container image and specifies the options PostgreSQL needs to locate and load the extension:
- :ref:`**Via Image Catalog** <#via-an-image-catalog-recommended>` : If the cluster references an ``ImageCatalog`` that
defines extensions for the current PostgreSQL major version, those
definitions serve as the default configuration. This allows the
catalog to centrally manage the ``image.reference`` , while the
cluster simply enables the extension by name.
- :ref:`Directly in the Cluster ` : If a cluster does not use an image catalog, the
``image.reference`` field is mandatory and must explicitly point to a
valid container registry path for the extension image. The ``image``
stanza follows the
`Kubernetes `ImageVolume` API `_ .
Following the *“convention over configuration”* paradigm, CloudNativePG
provides total flexibility: any value inherited from a catalog,
including the ``image.reference`` , can be selectively overridden within
the ``Cluster`` definition to meet specific local requirements.
Mounting and PostgreSQL Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each extension is mounted as a read-only volume at
``/extensions/`` inside the pod.
By default, CloudNativePG automatically manages the relevant GUCs by
setting:
- ``extension_control_path`` to ``/extensions//share`` ,
allowing PostgreSQL to locate any extension control file within
``/extensions//share/extension``
- ``dynamic_library_path`` to ``/extensions//lib``
.. Note::
Extension names containing underscores (e.g., `pg_ivm` ) are converted to use hyphens (e.g., `pg-ivm` ) for Kubernetes volume names to comply with RFC 1123 DNS label requirements. Do not use extension names that become identical after sanitization (e.g., `pg_ivm` and `pg-ivm` both sanitize to `pg-ivm` ). The webhook validation will prevent such conflicts. These values are appended in the order in which the extensions are defined in the `extensions` list, ensuring deterministic path resolution within PostgreSQL. This allows PostgreSQL to discover and load the extension without requiring manual configuration inside the pod.
While you can manually adjust these paths to match a custom image
layout, official CloudNativePG catalogs pre-configure these options to
the correct values for each extension, ensuring they work
out-of-the-box.
.. Important::
If an extension image includes shared libraries, they must be compiled for the same PostgreSQL major version, operating system distribution, and CPU architecture as the operand image. Using official CloudNativePG catalogs ensures this compatibility automatically: the catalogs are designed to match the specific environment of the cluster, preventing runtime issues caused by library or architecture mismatches.
Installation in a Database
^^^^^^^^^^^^^^^^^^^^^^^^^^
You can leverage :ref:`Managing Extensions in a Database `
to automate the final activation of any extension that supports the
standard PostgreSQL ``CREATE EXTENSION`` mechanism.
Once the extension image is mounted and the GUCs are configured, the
extension is available to the PostgreSQL instance but is not yet active
within a specific database. To install it, define a ``Database``
resource. The example below uses ``pgvector`` , following the
implementation standards of the `official `postgres-extensions-containers` project `_ :
.. code:: yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
name: cluster-example-app
spec:
name: app
owner: app
cluster:
name: cluster-example
extensions:
- name: vector
version: ""
CloudNativePG automatically reconciles this resource by executing
``CREATE EXTENSION IF NOT EXISTS vector`` within the target database.
This ensures your desired state is maintained and consistently applied
across all instances.
.. Note::
Some PostgreSQL components, often referred to as modules, do not use the `CREATE EXTENSION` mechanism. These typically consist of shared libraries that must be loaded via `shared_preload_libraries` at server start. Even when using an image catalog to simplify the distribution of these binaries, you must still manually add the library name to the :ref:`shared_preload_libraries `
in your ``Cluster`` definition to ensure it is loaded into memory.
Adding an Extension to a Postgres Cluster
-----------------------------------------
As anticipated earlier, CloudNativePG offers two ways to define
extension images. While both achieve the same result at the Pod level,
using an image catalog is the recommended approach for maintaining a
consistent, production-ready supply chain.
Via an Image Catalog (Recommended)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. Note::
Support for extension container images in image catalogs was introduced in CloudNativePG 1.29. When you use an :ref:`image catalog that covers extensions `
like the :ref:`official ones `
provided by the community, the complex configuration details, such as
the specific container image ``reference`` and the required filesystem
paths, are managed centrally.
Your ``Cluster`` definition remains clean and declarative, as it only
needs to “opt-in” to the extension by name. The operator automatically
handles the resolution of the image and the required PostgreSQL settings
based on the catalog’s definitions.
To enable an extension like ``pgvector`` from a catalog, add it to the
``extensions`` list in your cluster like in the following excerpt:
.. code:: yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
# ...
imageCatalogRef:
apiGroup: postgresql.cnpg.io
kind: ClusterImageCatalog
name: postgresql-minimal-trixie
major: 18
postgresql:
extensions:
- name: pgvector # Resolves all details, including the image reference, from the catalog
# ...
This method ensures that the extension image is always compatible with
the PostgreSQL operand image defined in the same catalog entry.
Directly in the Cluster
^^^^^^^^^^^^^^^^^^^^^^^
.. Note::
Defining extensions directly in the `Cluster` resource is the original method and remains the only option for versions prior to CloudNativePG 1.29. It is also useful if you need to use an extension not present in your current catalog or for testing custom images. You can define an extension directly within the `Cluster` resource without a catalog. In this case, you must explicitly provide the image `reference` .
The following example shows how to add ``pgvector`` by explicitly
pointing to its official container image:
.. code:: yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
# ...
postgresql:
extensions:
- name: pgvector
image:
reference: ghcr.io/cloudnative-pg/pgvector:
.. Tip::
Remember that configuration provided directly in the `Cluster` takes precedence. If you reference a catalog but also define the same extension name in the `Cluster` stanza, the settings in the `Cluster` will override those in the catalog. While every field in an :ref:`ExtensionConfiguration `
can be overridden at the ``Cluster`` level to provide total flexibility,
the ``name`` field is the exception. !!! Warning The ``name`` serves as
the unique identifier; changing it will define a new extension entry
rather than overriding an existing one from a catalog.
Verifying Extension Status
^^^^^^^^^^^^^^^^^^^^^^^^^^
Because extensions can be sourced from both image catalogs and direct
cluster definitions, CloudNativePG provides a “resolved” view of the
configuration in the ``Cluster`` status. This is the final, effective
state the operator uses to provision the pods.
You can inspect the ``status.pgDataImageInfo.extensions`` field to see
the results of any inheritance or overrides:
.. code:: yaml
# ...
status:
# ...
pgDataImageInfo:
image: # registry path for your PostgreSQL image
majorVersion: 18
extensions:
- name: foo
image:
reference: # registry path for your `foo` extension image
- name: bar
image:
reference: # registry path for your `bar` extension image
# ...
This section is particularly useful for:
- **Validating Resolution:** Confirming that an extension requested by
name in the ``Cluster`` has correctly pulled its ``image.reference``
from the associated catalog.
- **Confirming Overrides:** Verifying that a cluster-level override
(such as a specific image version) has successfully replaced the
catalog’s default value.
- **Troubleshooting:** Ensuring all desired extensions are recognized by
the operator before the pods undergo a rolling update.
Removing an Extension from a PostgreSQL Cluster
-----------------------------------------------
Removing an extension involves a two-step process to clean up both the
infrastructure and the database metadata.
You should first remove the extension from the database to avoid
“library not found” errors. If you are using declarative management,
update your ``Database`` resource by setting ``ensure: absent`` for the
extension:
.. code:: yaml
spec:
# ...
extensions:
- name: pgvector
ensure: absent
# ...
This triggers CloudNativePG to execute ``DROP EXTENSION`` within the
database.
If the extension was added to ``shared_preload_libraries`` , you must
also remove it from your ``Cluster`` configuration.
Then, remove the extension entry from the
``.spec.postgresql.extensions`` list in your ``Cluster`` resource. The
operator will perform a rolling update to detach the ``ImageVolume`` and
update the relevant GUC paths.
Advanced Topics
---------------
In some cases, the default expected structure may be insufficient for
your extension image, particularly when:
- The extension requires additional system libraries.
- Multiple extensions are bundled in the same image.
- The image uses a custom directory structure.
Following the *“convention over configuration”* paradigm, CloudNativePG
allows you to finely control the configuration of each extension image
through the following fields:
- ``extension_control_path`` : A list of relative paths within the
container image to be appended to PostgreSQL’s
``extension_control_path`` , allowing it to locate extension control
files.
- ``dynamic_library_path`` : A list of relative paths within the
container image to be appended to PostgreSQL’s
``dynamic_library_path`` , enabling it to locate shared library files
for extensions.
- ``ld_library_path`` : A list of relative paths within the container
image to be appended to the ``LD_LIBRARY_PATH`` environment variable
of the instance manager process, allowing PostgreSQL to locate
required system libraries at runtime.
This flexibility enables you to support complex or non-standard
extension images while maintaining clarity and predictability.
Setting Custom Paths
^^^^^^^^^^^^^^^^^^^^
If your extension image does not use the default ``lib`` and ``share``
directories for its libraries and control files, you can override the
defaults by explicitly setting ``extension_control_path`` and
``dynamic_library_path`` .
For example:
.. code:: yaml
spec:
# ...
postgresql:
extensions:
- name: my-extension
extension_control_path:
- my/share/path
dynamic_library_path:
- my/lib/path
image:
reference: # registry path for your extension image
# ...
# ...
# ...
CloudNativePG will configure PostgreSQL with:
- ``/extensions/my-extension/my/share/path`` appended to
``extension_control_path``
- ``/extensions/my-extension/my/lib/path`` appended to
``dynamic_library_path``
This allows PostgreSQL to discover your extension’s control files and
shared libraries correctly, even with a non-standard layout.
Multi-extension Images
^^^^^^^^^^^^^^^^^^^^^^
You may need to include multiple extensions within the same container
image, adopting a structure where each extension’s files reside in their
own subdirectory.
For example, to package PostGIS and pgRouting together in a single
image, each in its own subdirectory:
.. code:: yaml
# ...
spec:
# ...
postgresql:
extensions:
- name: geospatial
extension_control_path:
- postgis/share
- pgrouting/share
dynamic_library_path:
- postgis/lib
- pgrouting/lib
# ...
image:
reference: # registry path for your geospatial image
# ...
# ...
# ...
Including System Libraries
^^^^^^^^^^^^^^^^^^^^^^^^^^
Some extensions, such as PostGIS, require system libraries that may not
be present in the base PostgreSQL image. To support these requirements,
you can package the necessary libraries within your extension container
image and make them available to PostgreSQL using the
``ld_library_path`` field.
For example, if your extension image includes a ``system`` directory
with the required libraries:
.. code:: yaml
# ...
spec:
# ...
postgresql:
extensions:
- name: postgis
# ...
ld_library_path:
- system
image:
reference: # registry path for your PostGIS image
# ...
# ...
# ...
CloudNativePG will set the ``LD_LIBRARY_PATH`` environment variable to
include ``/extensions/postgis/system`` , allowing PostgreSQL to locate
and load these system libraries at runtime.
.. Important::
Since `ld_library_path` must be set when the PostgreSQL process starts, changing this value requires a **cluster restart** for the new value to take effect. CloudNativePG does not currently trigger this restart automatically; you will need to manually restart the cluster (e.g., using `cnpg restart` ) after modifying `ld_library_path` .
Image Specifications
--------------------
A standard extension container image for CloudNativePG includes two
required directories at its root:
- ``/share/`` : contains an ``extension`` subdirectory with the
extension control file (e.g. ``.control`` ) and the
corresponding SQL files.
- ``/lib/`` : contains the extension’s shared library
(e.g. ``.so`` ) as well as any other required libraries.
Following this structure ensures that the extension will be
automatically discoverable and usable by PostgreSQL within CloudNativePG
without requiring manual configuration.
.. Important::
We encourage PostgreSQL extension developers and third-party providers to publish OCI-compliant extension images following this layout. For practical implementation details, we recommend reviewing the `postgres-extensions-containers `_ , which serves as the reference for building official CloudNativePG extension images.
Ideally, extension images should:
- Target a specific operating system distribution and set of CPU
architectures.
- Be tied to a particular PostgreSQL major version.
- Be built using the distribution’s native packaging system (e.g.,
``.deb`` or ``.rpm`` packages) to ensure consistency, security, and
compatibility with the PostgreSQL operand images used in your
clusters.
Caveats
-------
Currently, adding, removing, or updating an extension image triggers a
restart of the PostgreSQL pods. This behavior is inherited from how
`image volumes `_
work in Kubernetes.
Before performing an extension update, ensure you have:
- Thoroughly tested the update process in a staging environment.
- Verified that the extension image contains the required upgrade path
between the currently installed version and the target version.
- Updated the ``version`` field for the extension in the relevant
``Database`` resource definition to align with the new version in the
image.
These steps help prevent downtime or data inconsistencies in your
PostgreSQL clusters during extension updates.