Integrate TimescaleDB with CloudNativePG on Kubernetes
Deploy and operate TimescaleDB on Kubernetes with CloudNativePG for production-ready time-series workloads
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 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)
kubectlconfigured- A default StorageClass
- Cluster-admin permissions
- A running Kubernetes node with persistent storage
Architecture overview
Section titled “Architecture overview”The deployment architecture is intentionally simple.
+------------------------------------------------+| Kubernetes Cluster |+------------------------------------------------+ | ▼ CloudNativePG Operator | ▼ PostgreSQL Cluster (CNPG) | ▼ TimescaleDB OSS Extension Container | ▼ Time-Series Applications & ServicesCloudNativePG 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?
Section titled “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
Section titled “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:
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=trueIf the feature gate is not enabled, extension containers will not function correctly.
Create a local Kubernetes cluster with Kind
Section titled “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.
- Create a configuration file
Create a configuration file named
kind-config.yaml.kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4name: opsfeatureGates:ImageVolume: true - Create the cluster
Terminal window kind create cluster --config kind-config.yaml - Check the nodes
Terminal window kubectl get nodesYou should see something similar to:
NAME STATUS ROLES AGE VERSIONops-control-plane Ready control-plane 1m v1.34.x
Install CloudNativePG
Section titled “Install CloudNativePG”At the time of writing, CloudNativePG 1.30 is the latest stable release in the 1.30 series.
- 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 - Verify that the operator is running
Verify that the operator is running:
Terminal window kubectl rollout status deployment -n cnpg-system cnpg-controller-managerOnce the deployment is successfully rolled out, your Kubernetes cluster is ready to manage PostgreSQL resources.
Deploy TimescaleDB
Section titled “Deploy TimescaleDB”- Create the PostgreSQL cluster
-
Create a file named
cluster-timescaledb.yaml:apiVersion: postgresql.cnpg.io/v1kind: Clustermetadata:name: cluster-timescaledbspec:imageName: ghcr.io/cloudnative-pg/postgresql:18-system-trixieinstances: 1storage:size: 1Gipostgresql:shared_preload_libraries:- "timescaledb"parameters:timescaledb.telemetry_level: 'off'max_locks_per_transaction: '128'extensions:- name: timescaledb-ossimage:reference: ghcr.io/cloudnative-pg/timescaledb-oss:2.27.2-18-trixieKey configuration details:
2.27.2is the TimescaleDB version.18represents the PostgreSQL major version.trixieindicates the Debian base image.
-
Deploy the cluster:
Terminal window kubectl apply -f cluster-timescaledb.yamlCloudNativePG provisions the PostgreSQL instance and automatically mounts the TimescaleDB extension container.
-
- Create a database
-
Create
database-timescaledb.yaml:apiVersion: postgresql.cnpg.io/v1kind: Databasemetadata:name: cluster-timescaledb-appspec:name: appowner: appcluster:name: cluster-timescaledbextensions:- name: timescaledbversion: "2.27.2" -
Apply it:
Terminal window kubectl apply -f database-timescaledb.yamlCloudNativePG creates the database and enables the TimescaleDB extension automatically.
-
- Verify the installation
After deploying the cluster and database resources, connect to PostgreSQL and verify that the TimescaleDB extension has been installed successfully.
-
Open a shell inside the PostgreSQL pod:
Terminal window kubectl exec -it cluster-timescaledb-1 -- sh -
Launch the PostgreSQL client:
Terminal window psql -
Connect to the application database:
\c app -
List installed extensions:
\dxYou should see output similar to:
List of installed extensionsName | Version | Schema | Description-------------+---------+------------+------------------------------plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural languagetimescaledb | 2.27.2 | public | TimescaleDB OSSThe TimescaleDB extension is now active and ready for use.
-
Create your first hypertable
Section titled “Create your first hypertable”- 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.
- Insert sample data
Populate the table with some test data:
INSERT INTO sensor_dataSELECTNOW() - (g || ' minutes')::interval,(random()*10)::int,random()*30,random()*100FROM generate_series(1,10000) g; - Query time-series data
Use one of TimescaleDB's most popular functions to calculate the average temperature per hour:
SELECTtime_bucket('1 hour', time) AS bucket,avg(temperature) AS avg_temperatureFROM sensor_dataGROUP BY bucketORDER 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
Section titled “Production considerations”This guide intentionally keeps the deployment simple. For production environments, you'll typically want to enable additional capabilities.
High availability
Section titled “High availability”Deploy multiple PostgreSQL instances.
instances: 3CloudNativePG automatically manages leader election and failover.
Automated backups
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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.