Skip to content

CloudNativePG Factories

Factory functions for CloudNativePG Custom Resource Definitions with built-in readiness evaluation. Manage PostgreSQL clusters, backups, and connection pooling as Kubernetes-native resources.

Import

typescript
// Import specific functions (recommended)
import { cluster, backup, scheduledBackup, pooler } from 'typekro/cnpg';

// Or namespace import
import * as cnpg from 'typekro/cnpg';

Quick Example

typescript
import { cluster, scheduledBackup, pooler } from 'typekro/cnpg';

// Create a 3-instance PostgreSQL cluster
const db = cluster({
  name: 'my-database',
  namespace: 'databases',
  spec: {
    instances: 3,
    storage: { size: '50Gi', storageClass: 'gp3' },
    postgresql: {
      parameters: { shared_buffers: '256MB', max_connections: '200' },
    },
    bootstrap: {
      initdb: { database: 'myapp', owner: 'myapp', encoding: 'UTF8' },
    },
  },
  id: 'primaryDatabase',
});

// Schedule nightly backups
const nightly = scheduledBackup({
  name: 'nightly-backup',
  namespace: 'databases',
  spec: {
    cluster: { name: 'my-database' },
    schedule: '0 0 2 * * *',
    immediate: true,
    backupOwnerReference: 'cluster',
  },
  id: 'nightlyBackup',
});

// Add PgBouncer connection pooling
const pool = pooler({
  name: 'my-database-pooler',
  namespace: 'databases',
  spec: {
    cluster: { name: 'my-database' },
    type: 'rw',
    instances: 2,
    pgbouncer: {
      poolMode: 'transaction',
      parameters: { default_pool_size: '25' },
    },
  },
  id: 'dbPooler',
});

Available Factories

FactoryKindScopeDescription
clusterClusterNamespacePostgreSQL cluster (primary + replicas)
backupBackupNamespaceOn-demand backup
scheduledBackupScheduledBackupNamespaceCron-based automated backups
poolerPoolerNamespacePgBouncer connection pooling
cnpgHelmRepositoryHelmRepositoryNamespaceHelm chart repository for CNPG
cnpgHelmReleaseHelmReleaseNamespaceOperator installation via Helm

cluster()

Creates a PostgreSQL cluster managed by the CNPG operator.

typescript
const db = cluster({
  name: 'prod-db',
  namespace: 'databases',
  spec: {
    instances: 3,
    imageName: 'ghcr.io/cloudnative-pg/postgresql:16.2',
    storage: {
      size: '100Gi',
      storageClass: 'gp3',
    },
    postgresql: {
      parameters: {
        shared_buffers: '256MB',
        max_connections: '200',
        work_mem: '8MB',
      },
      pg_hba: ['host all all 10.0.0.0/8 md5'],
    },
    bootstrap: {
      initdb: {
        database: 'myapp',
        owner: 'myapp',
        encoding: 'UTF8',
        dataChecksums: true,
      },
    },
    backup: {
      barmanObjectStore: {
        destinationPath: 's3://my-backups/prod-db',
        s3Credentials: {
          accessKeyId: { name: 'aws-creds', key: 'ACCESS_KEY_ID' },
          secretAccessKey: { name: 'aws-creds', key: 'SECRET_ACCESS_KEY' },
        },
      },
      retentionPolicy: '30d',
    },
    resources: {
      requests: { cpu: '500m', memory: '1Gi' },
      limits: { cpu: '2', memory: '4Gi' },
    },
    affinity: {
      enablePodAntiAffinity: true,
      topologyKey: 'kubernetes.io/hostname',
      podAntiAffinityType: 'required',
    },
    monitoring: { enabled: true },
  },
  id: 'prodDatabase',
});

Cluster Readiness

The cluster readiness evaluator tracks CNPG-specific lifecycle phases:

PhaseReadyReason
Cluster in healthy statetrueHealthy
Setting up primaryfalseSettingUpPrimary
Creating replicafalseCreatingReplica
Failing overfalseFailover
Switchover in progressfalseFailover*
Unknown phaseFalls back to condition-based evaluation

*Switchover (planned) and failover (unplanned) are both transient non-ready states. They share the Failover reason since both represent a primary transition in progress.

Bootstrap Methods

typescript
// Initialize a new database
bootstrap: { initdb: { database: 'myapp', owner: 'myapp' } }

// Recover from backup (PITR)
bootstrap: {
  recovery: {
    source: 'external-cluster-name',
    recoveryTarget: { targetTime: '2024-01-15T10:00:00Z' },
  },
}

// Clone from pg_basebackup
bootstrap: { pg_basebackup: { source: 'source-cluster' } }

backup()

Creates an on-demand backup.

typescript
const bk = backup({
  name: 'manual-backup',
  namespace: 'databases',
  spec: {
    cluster: { name: 'prod-db' },
    method: 'barmanObjectStore',
    target: 'prefer-standby',
  },
  id: 'manualBackup',
});

Backup Readiness

PhaseReadyReason
completedtrueCompleted
startedfalseInProgress
failedfalseFailed
newfalsePending

scheduledBackup()

Creates a cron-scheduled backup. Uses robfig/cron format with seconds: second minute hour day month day-of-week.

typescript
const nightly = scheduledBackup({
  name: 'nightly-backup',
  namespace: 'databases',
  spec: {
    cluster: { name: 'prod-db' },
    schedule: '0 0 2 * * *',     // 2 AM daily
    immediate: true,              // Run first backup immediately
    method: 'volumeSnapshot',
    backupOwnerReference: 'cluster',
  },
  id: 'nightlyBackup',
});

pooler()

Creates a PgBouncer connection pooler.

typescript
const pool = pooler({
  name: 'prod-pooler',
  namespace: 'databases',
  spec: {
    cluster: { name: 'prod-db' },
    type: 'rw',
    instances: 3,
    pgbouncer: {
      poolMode: 'transaction',
      parameters: {
        default_pool_size: '25',
        max_client_conn: '200',
      },
    },
  },
  id: 'prodPooler',
});

Pool Modes

  • session (default) — Connection assigned for the entire client session
  • transaction — Connection returned to pool after each transaction (recommended for most apps)

Bootstrap Composition

Install the CNPG operator via Helm:

typescript
import { cnpgBootstrap } from 'typekro/cnpg';

// KRO mode — operator reconciled continuously
const factory = cnpgBootstrap.factory('kro', {
  namespace: 'cnpg-system',
  waitForReady: true,
});

await factory.deploy({
  name: 'cnpg',
  namespace: 'cnpg-system',
  version: '0.23.0',
  installCRDs: true,
});

Bootstrap Status

typescript
instance.status.ready    // boolean — operator is running
instance.status.phase    // 'Ready' | 'Installing' (derived from HelmRelease condition)
instance.status.version  // deployed chart version

Usage in Compositions

typescript
import { type } from 'arktype';
import { kubernetesComposition } from 'typekro';
import { Deployment, Service } from 'typekro/simple';
import { cluster, pooler } from 'typekro/cnpg';

const AppWithDB = kubernetesComposition({
  name: 'app-with-db',
  kind: 'AppWithDB',
  spec: type({ name: 'string', image: 'string', dbSize: 'string' }),
  status: type({ ready: 'boolean', dbReady: 'boolean' }),
}, (spec) => {
  const db = cluster({
    id: 'database',
    name: `${spec.name}-db`,
    spec: {
      instances: 3,
      storage: { size: spec.dbSize },
      bootstrap: { initdb: { database: spec.name, owner: 'app' } },
    },
  });

  const pool = pooler({
    id: 'pooler',
    name: `${spec.name}-pooler`,
    spec: {
      cluster: { name: `${spec.name}-db` },
      type: 'rw',
      pgbouncer: { poolMode: 'transaction' },
    },
  });

  const deploy = Deployment({
    id: 'app',
    name: spec.name,
    image: spec.image,
    env: {
      DATABASE_URL: `postgresql://app@${spec.name}-pooler:5432/${spec.name}`,
    },
  });

  return {
    ready: deploy.status.readyReplicas > 0,
    dbReady: db.status.readyInstances >= 3,
  };
});

Prerequisites

The CloudNativePG operator must be installed in your cluster. Use the cnpgBootstrap composition or install manually:

bash
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm install cnpg cnpg/cloudnative-pg \
  --namespace cnpg-system \
  --create-namespace

Next Steps

Released under the Apache 2.0 License.