---
title: Integrate TimescaleDB with CloudNativePG on Kubernetes | Tiger Data Docs
description: Deploy and operate TimescaleDB on Kubernetes with CloudNativePG for production-ready time-series workloads
---

![](https://avatars.githubusercontent.com/shusaan?s=128)

Husn E Rabbi

[GitHub](https://github.com/shusaan) · [LinkedIn](http://www.linkedin.com/in/husn-e-rabbi)

Managing PostgreSQL on Kubernetes has come a long way. While deploying a database is relatively straightforward, operating it reliably in production is where the real challenge begins.

Backups, failover, upgrades, monitoring, and disaster recovery all require careful planning. Traditionally, this meant combining Helm charts with custom scripts or manually managing PostgreSQL images as requirements evolved.

[CloudNativePG](https://cloudnative-pg.io/) takes a different approach.

Rather than treating PostgreSQL as just another container, CloudNativePG manages it as a Kubernetes-native resource. You declare the desired state of your PostgreSQL cluster, and the operator continuously reconciles the cluster to match it. Tasks such as provisioning, failover, backups, rolling updates, and lifecycle management become automated.

With CloudNativePG's PostgreSQL extension containers, you can also deploy TimescaleDB without maintaining custom PostgreSQL images. PostgreSQL and its extensions are managed independently, making upgrades significantly easier.

This integration guide walks you through deploying TimescaleDB on CloudNativePG, enabling the extension, and creating your first hypertable.

## Prerequisites for this integration guide

To follow these steps, you'll need:

- Kubernetes cluster (v1.31 or later recommended)
- `kubectl` configured
- A default StorageClass
- Cluster-admin permissions
- A running Kubernetes node with persistent storage

## Architecture overview

The deployment architecture is intentionally simple.

```
+------------------------------------------------+
|                Kubernetes Cluster              |
+------------------------------------------------+
                    |
                    ▼
        CloudNativePG Operator
                    |
                    ▼
         PostgreSQL Cluster (CNPG)
                    |
                    ▼
    TimescaleDB OSS Extension Container
                    |
                    ▼
     Time-Series Applications & Services
```

CloudNativePG manages the PostgreSQL cluster lifecycle, while the TimescaleDB extension container provides the required extension binaries. This separation allows PostgreSQL and extensions to evolve independently.

## Why extension containers?

Historically, installing PostgreSQL extensions in Kubernetes often required building and maintaining custom PostgreSQL images.

That approach introduces several operational challenges:

- Custom image maintenance
- Rebuilding images for PostgreSQL upgrades
- Managing extension compatibility
- Larger CI/CD pipelines
- More complex image security scanning

CloudNativePG extension containers eliminate these problems. Instead of embedding every extension into the PostgreSQL image, extensions are packaged separately. During cluster initialization, CloudNativePG mounts the extension container into PostgreSQL, making the extension available without modifying the base database image.

This keeps PostgreSQL images small, simplifies upgrades, and makes extension lifecycle management significantly cleaner.

## Verify Kubernetes feature gates

CloudNativePG extension containers rely on the Kubernetes `ImageVolume` feature. Ensure your Kubernetes cluster supports this feature before continuing.

If you're using a managed Kubernetes service, consult your provider's documentation to verify support.

To confirm the API server has the feature gate enabled, run:

Terminal window

```
kubectl get pods -n kube-system \
  -l component=kube-apiserver \
  -o yaml | grep -A 20 "containers:" | grep -E "args:|feature-gates"
```

You should see output similar to:

```
--feature-gates=ImageVolume=true
```

If the feature gate is not enabled, extension containers will not function correctly.

## Create a local Kubernetes cluster with Kind

If you don't already have a Kubernetes cluster available, Kind (Kubernetes in Docker) is one of the easiest ways to create a local development environment.

1. **Create a configuration file**

   Create a configuration file named `kind-config.yaml`.

   ```
   kind: Cluster
   apiVersion: kind.x-k8s.io/v1alpha4
   name: ops
   featureGates:
     ImageVolume: true
   ```

2. **Create the cluster**

   Terminal window

   ```
   kind create cluster --config kind-config.yaml
   ```

3. **Check the nodes**

   Terminal window

   ```
   kubectl get nodes
   ```

   You should see something similar to:

   ```
   NAME                 STATUS   ROLES           AGE   VERSION
   ops-control-plane    Ready    control-plane   1m    v1.34.x
   ```

## Install CloudNativePG

At the time of writing, CloudNativePG 1.30 is the latest stable release in the 1.30 series.

1. **Install the operator**

   Install the operator:

   Terminal window

   ```
   kubectl apply --server-side -f \
     https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
   ```

2. **Verify that the operator is running**

   Verify that the operator is running:

   Terminal window

   ```
   kubectl rollout status deployment -n cnpg-system cnpg-controller-manager
   ```

   Once the deployment is successfully rolled out, your Kubernetes cluster is ready to manage PostgreSQL resources.

## Deploy TimescaleDB

1. **Create the PostgreSQL cluster**

   1. Create a file named `cluster-timescaledb.yaml`:

      ```
      apiVersion: postgresql.cnpg.io/v1
      kind: Cluster
      metadata:
        name: cluster-timescaledb
      spec:
        imageName: ghcr.io/cloudnative-pg/postgresql:18-system-trixie
        instances: 1
        storage:
          size: 1Gi
        postgresql:
          shared_preload_libraries:
            - "timescaledb"
          parameters:
            timescaledb.telemetry_level: 'off'
            max_locks_per_transaction: '128'
          extensions:
            - name: timescaledb-oss
              image:
                reference: ghcr.io/cloudnative-pg/timescaledb-oss:2.27.2-18-trixie
      ```

      Key configuration details:

      - `2.27.2` is the TimescaleDB version.
      - `18` represents the PostgreSQL major version.
      - `trixie` indicates the Debian base image.

   2. Deploy the cluster:

      Terminal window

      ```
      kubectl apply -f cluster-timescaledb.yaml
      ```

      CloudNativePG provisions the PostgreSQL instance and automatically mounts the TimescaleDB extension container.

2. **Create a database**

   1. Create `database-timescaledb.yaml`:

      ```
      apiVersion: postgresql.cnpg.io/v1
      kind: Database
      metadata:
        name: cluster-timescaledb-app
      spec:
        name: app
        owner: app
        cluster:
          name: cluster-timescaledb
        extensions:
          - name: timescaledb
            version: "2.27.2"
      ```

   2. Apply it:

      Terminal window

      ```
      kubectl apply -f database-timescaledb.yaml
      ```

      CloudNativePG creates the database and enables the TimescaleDB extension automatically.

3. **Verify the installation**

   After deploying the cluster and database resources, connect to PostgreSQL and verify that the TimescaleDB extension has been installed successfully.

   1. Open a shell inside the PostgreSQL pod:

      Terminal window

      ```
      kubectl exec -it cluster-timescaledb-1 -- sh
      ```

   2. Launch the PostgreSQL client:

      Terminal window

      ```
      psql
      ```

   3. Connect to the application database:

      ```
      \c app
      ```

   4. List installed extensions:

      ```
      \dx
      ```

      You should see output similar to:

      ```
                     List of installed extensions
          Name     | Version |   Schema   |         Description
      -------------+---------+------------+------------------------------
       plpgsql     | 1.0     | pg_catalog | PL/pgSQL procedural language
       timescaledb | 2.27.2  | public     | TimescaleDB OSS
      ```

      The TimescaleDB extension is now active and ready for use.

## Create your first hypertable

1. **Create a hypertable**

   Create a hypertable for sensor readings:

   ```
   CREATE TABLE sensor_data (
     time TIMESTAMPTZ NOT NULL,
     sensor_id INTEGER,
     temperature DOUBLE PRECISION,
     humidity DOUBLE PRECISION
   ) WITH (tsdb.hypertable, tsdb.partition_column = 'time');
   ```

   Unlike regular PostgreSQL tables, hypertables automatically partition data by time while presenting a single logical table to applications.

2. **Insert sample data**

   Populate the table with some test data:

   ```
   INSERT INTO sensor_data
   SELECT
       NOW() - (g || ' minutes')::interval,
       (random()*10)::int,
       random()*30,
       random()*100
   FROM generate_series(1,10000) g;
   ```

3. **Query time-series data**

   Use one of TimescaleDB's most popular functions to calculate the average temperature per hour:

   ```
   SELECT
     time_bucket('1 hour', time) AS bucket,
     avg(temperature) AS avg_temperature
   FROM sensor_data
   GROUP BY bucket
   ORDER BY bucket;
   ```

   The `time_bucket()` function groups timestamps into fixed intervals, making time-series aggregations both intuitive and efficient. This is one of the key capabilities that distinguishes TimescaleDB from standard PostgreSQL.

## Production considerations

This guide intentionally keeps the deployment simple. For production environments, you'll typically want to enable additional capabilities.

### High availability

Deploy multiple PostgreSQL instances.

```
instances: 3
```

CloudNativePG automatically manages leader election and failover.

### Automated backups

CloudNativePG supports several backup strategies:

- S3-compatible object storage
- Google Cloud Storage
- Azure Blob Storage
- CSI VolumeSnapshots

Choose the approach that aligns with your storage platform and recovery objectives.

### Point-in-time recovery

CloudNativePG supports restoring your database to an exact point in time using archived WAL files.

This capability is essential for recovering from accidental deletes or application errors.

### Monitor

Production databases should expose metrics to Prometheus and visualize them using Grafana dashboards.

Typical metrics include:

- Query latency
- Replication lag
- Storage usage
- WAL generation
- Connection count

### Storage

Database performance depends heavily on storage.

Use SSD-backed persistent volumes with an appropriate StorageClass, and size volumes based on expected data growth and retention requirements.

### Upgrade PostgreSQL and TimescaleDB

One advantage of CloudNativePG extension containers is that PostgreSQL and extension upgrades become independent operations.

Instead of rebuilding custom PostgreSQL images every time a new TimescaleDB release becomes available, you can update the extension container version while allowing CloudNativePG to manage PostgreSQL upgrades according to its lifecycle.

This significantly reduces operational overhead and helps keep both PostgreSQL and TimescaleDB current with minimal effort.

## Conclusion

CloudNativePG and TimescaleDB complement each other exceptionally well.

CloudNativePG simplifies PostgreSQL operations through Kubernetes-native automation, while TimescaleDB extends PostgreSQL with powerful time-series capabilities such as hypertables and time-based analytics.

Together they provide a clean, declarative, and production-ready platform for building time-series applications on Kubernetes.

By leveraging CloudNativePG's extension container framework, you can avoid maintaining custom PostgreSQL images, simplify upgrades, and keep your deployment aligned with cloud-native best practices.

Whether you're collecting IoT telemetry, monitoring infrastructure, processing financial events, or analyzing application metrics, this architecture provides a scalable foundation with minimal operational complexity.
