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.