Skip to main content

Overview

CDC Pipeline deploys a complete change data capture stack in a single release. It reimplements nothing: it bundles three catalog templates as dependencies — PostgreSQL Highly Available as the source database, Kafka as the event transport, and Debezium Server as the connector that reads the database’s change stream and publishes every row-level insert, update and delete to Kafka. This chart adds only the glue: a policy letting the Kafka brokers read the one credentials secret you create, the internal hostnames each component uses to reach the others, and checks at render time that the components’ values agree.
This template deploys into an existing GVC that you already have. It does not create or manage a GVC. Every workload runs in each location the GVC has, so use a GVC with a single location.
Version 1.0.4 does not deliver change events with its default configuration. The bundled PostgreSQL cluster starts with wal_level = replica, while Debezium’s pgoutput decoding needs wal_level = logical; database.walLevel is checked at render but is not applied to the database. Kafka also enforces ACLs and the chart grants none to the debezium user. Both are described under Troubleshooting. Treat this release as a starting point you must finish configuring, and verify events arrive before you rely on it.

What Gets Created

Prerequisites

1

Create the credentials secret

One dictionary secret must exist before you install. PostgreSQL initializes with it, the Kafka brokers take their SASL passwords from it, and Debezium connects with it, so it holds eight keys. my-cdc-pipeline-credentials is the name the values default to:
  • sourcePassword must equal password (Debezium reads that key), and username/database must equal debezium-server.source.database.user/.name. database is used unquoted, so keep it to lowercase letters, digits and underscores.
  • kafkaSaslPassword is the password of Kafka’s debezium user, on the broker side and in Debezium alike. kafkaAdminPassword is the Kafka admin user’s; the other two secure the brokers’ own connections. Keep the Kafka passwords to letters and digits, as generated above: they are placed inside Kafka’s login configuration, where a " or ; breaks it.
  • The secret’s name appears in six values: postgres-highly-available.config.credentialsSecretName, debezium-server.credentialsSecretName and the four kafka.kafka.env entries (see Database Credentials). All six must name the same secret; the chart refuses anything else. Secret names are org-wide, so give each release its own, and do not use RELEASE_NAME-debezium-credentials, which Debezium creates itself.
If the secret does not exist at install time, the deployment wedges silently: cpln logs returns zero lines, because the containers never start. The only diagnostic is status.versions[].message (also for RELEASE_NAME-cluster and RELEASE_NAME-debezium):
Plain cpln workload get has no versions key. Create the secret and the deployment recovers on its own after several minutes (poll rather than giving up), or skip the wait with cpln workload force-redeployment.
2

Optional: create the Kafbat UI configuration secret

Kafbat UI is off by default. It reads its whole configuration from an opaque secret mounted as a file, which must exist before you turn it on, and it is public when on. Replace ADMIN_PASSWORD with the credentials secret’s kafkaAdminPassword (read it with cpln secret reveal my-cdc-pipeline-credentials -o yaml) and UI_PASSWORD with the login you want for the console. The full file format is in the Kafbat configuration reference.
Set kafka.kafbat_ui.enabled to true and kafka.kafbat_ui.configuration_secret to this name. If you do not want the console, skip this step.
If the secret is missing, the install still succeeds, cpln logs returns nothing for RELEASE_NAME-kafbat-ui, and the workload never becomes ready. The only place the missing secret is named is status.versions[].message:
Create the secret and the deployment recovers on its own after a few minutes, or force it with cpln workload force-redeployment RELEASE_NAME-kafbat-ui --gvc GVC_NAME.
3

Choose the KRaft cluster ID

The KRaft cluster ID is a template value, and its default change-me-cdc-pipeline-kraft-cluster-id is a placeholder, so replace it. Generate it with the Kafka tooling, using the same image the brokers run:
The cluster ID is written into the Kafka log volumes at first start and cannot be changed afterwards without a new cluster.

Installation

Install into an existing GVC once the credentials secret exists. With the default secret name my-cdc-pipeline-credentials, only the KRaft cluster ID is required:
With a different secret name, set all six values that name it in a values file (-f values.yaml), as shown under Database Credentials.

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Each block shows the shipped defaults for one area of values.yaml. Only the keys you change need to go in your values file. The bundled components take their own values under postgres-highly-available, kafka and debezium-server; the linked component pages explain each option, but this release pins older component versions than those pages describe.

Database Credentials

Every credential of the pipeline comes from the one secret created in Prerequisites. Its name appears in six values, and all six must name the same secret:

PostgreSQL Highly Available

See PostgreSQL Highly Available for what each option does.

Kafka

The broker, listener, ACL and secret settings, plus the optional consoles. Every Kafka password is a ${env:VAR} reference, never the password: the broker resolves it from one of the CDC_KAFKA_* variables in kafka.kafka.env (Kafka’s EnvVarConfigProvider), and the chart refuses a plain password. kafka.jmx_exporter and kafka.kafka_client ship with working defaults; see Kafka for them.
To add another Kafka user, put its password in the credentials secret under a new key, add a kafka.kafka.env entry reading it (cpln://secret/SECRET_NAME.NEW_KEY), and append the user and ${env:THAT_VARIABLE} to users and passwords. Kafka Connect (kafka.kafka_connectors) is refused at render, because its worker login and plugin configuration take credentials inline; Debezium Server is this pipeline’s connector, so install the Kafka template on its own to run Kafka Connect.

Debezium Server

The connector’s source and sink. hostname and bootstrapServers are filled in from the release name when left empty, so the defaults already point at the bundled database and brokers.
Before the connector starts, its startup step creates the debezium_heartbeat table and, once wal_level is logical, the debezium replication slot in the source database, so the heartbeat needs no manual SQL. Pointing hostname or bootstrapServers at a database or Kafka outside this release still deploys the bundled components, and the render-time credential checks still apply. Debezium reads its database and Kafka passwords from the credentials secret named by debezium-server.credentialsSecretName.

Connecting

Nothing in the pipeline is public except Kafbat UI. Everything else is reachable from inside the GVC. Read the Kafbat UI endpoint from the workload:
To exercise the pipeline end to end, first write to the source database. Tunnel to HAProxy from your own machine:
Then, in a second terminal, create a table and insert a row:
Next, read the change events from Kafka with the bundled client workload. Open a shell on it:
Inside it, write the admin credentials to a client properties file, list the topics and consume the table’s topic:
If no dbserver1. topic appears, read the connector log and see Troubleshooting:
Applications in the GVC consume the same way, with these client properties and the bootstrap address above. To give a consumer its own Kafka user rather than admin, add it as described under Kafka (a new key in the credentials secret, a kafka.kafka.env entry, and the user and its ${env:VAR} reference appended to users and passwords), and give it access, either with Kafka ACLs or by listing it in kafka.kafka.acl.superUsers.

Operations

Backing Up

Every volume set in the pipeline takes a final snapshot when it is deleted and keeps it for seven days; the Kafka log volume sets also take a snapshot daily at 00:00 UTC. Take extra snapshots before a risky change. The source database is the system of record:
The Debezium offsets on RELEASE_NAME-debezium-data record how far the connector has read; losing them makes it snapshot the source again from the start:
The bundled PostgreSQL template’s own backups (pg_dumpall or WAL-G to object storage) are switched off here with postgres-highly-available.backup.enabled: false. Turning them on through this chart has not been verified; see PostgreSQL Highly Available for how they work in the standalone template.

Restoring a Backup

Restoring any of this pipeline’s volume snapshots has not been verified. A restored database whose replication slot no longer matches the Debezium offsets, or Kafka brokers whose metadata no longer matches their peers, may not resume cleanly. Rely on PostgreSQL and Kafka replication for the loss of a single member, and treat snapshots as a last resort.

Upgrading from 1.0.1

Versions 1.0.0 and 1.0.1 bundle a PostgreSQL Highly Available release that does not compact its etcd cluster, so etcd’s backend grows with time alone until it reaches its 2 GiB quota and goes read-only, taking PostgreSQL failover with it. Every install of those versions is affected, because the database cannot be switched off. Upgrading to 1.0.2 or later turns compaction on and stops further growth, but it cannot shrink a backend that has already grown, and a cluster that has already raised a NOSPACE alarm needs operator recovery rather than an upgrade. The mechanism and the checks are on the PostgreSQL Highly Available page under etcd History Compaction.

Upgrading from 1.0.3

Version 1.0.4 moved the database credentials. They used to be postgres-highly-available.postgres.username, .password, .database and .walLevel; they are now database.username, database.password, database.name and database.walLevel, and this chart creates the credentials secret named by postgres-highly-available.config.credentialsSecretName. Old keys left under postgres-highly-available.postgres are no longer read. An existing database keeps the credentials it was created with, so set database.* to those exact values; different values leave the members unable to authenticate after their next restart. Recover them before upgrading, from your values file or from the secret the earlier version created:
This upgrade path has not been verified end to end. Snapshot the database volume set first, as shown under Backing Up. Then follow Upgrading from 1.0.4.

Upgrading from 1.0.4

1.0.4 never delivered change events (wal_level was replica and Debezium had no Kafka permissions), so a fresh install of 1.1.1 is the simplest path: install it as a new release, move any data the database holds, then uninstall the old release. To upgrade in place instead:
  1. 1.0.4 created the database secret itself, and this upgrade deletes it; it also kept Kafka’s passwords as values, which 1.1.x reads from the secret instead. Before upgrading, create the new secret under a new name holding the values the database and the Kafka cluster run with today, not new ones — the running cluster keeps its passwords, and nothing changes on it. Read the database credentials from my-cdc-pipeline-db-credentials (or the name in postgres-highly-available.config.credentialsSecretName), the Kafka client-admin-password, inter-broker-password and controller-password from RELEASE_NAME-secrets, and Debezium’s kafka-sasl-password from RELEASE_NAME-debezium-credentials:
  2. In your values, delete the database: block, debezium-server.source.database.password and debezium-server.sink.kafka.saslPassword; set both credentialsSecretName keys to the new name; and if you changed kafka.kafka.acl.superUsers, add User:debezium. Replace every Kafka password with its reference and add the kafka.kafka.env entries and provider lines exactly as in Kafka and Database Credentials, naming the new secret; delete any kafka.kafka_exporter block (1.1.x runs no Kafka exporter or health sidecar). The chart refuses the old keys and any plain Kafka password at render.
  3. Before upgrading, switch the running cluster to wal_level = logical — 1.0.4 created it with replica, and 1.1.x replicas refuse to start on a replica cluster, which stalls the upgrade. The same command tells Patroni to leave logical slots alone and removes the install-time password 1.0.4 stored in the cluster configuration ("authentication":null; without it this command prints that password back to you). Then restart the members in place, the replicas first and the leader last (patronictl does not work on 1.0.4’s cluster, so this uses Patroni’s API; LOCATION is your GVC’s location):
    Run the cluster call again: every member should be running or streaming with no pending_restart.
  4. Upgrade. The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so Debezium can briefly lose its database; it reconnects by itself. The Kafka brokers restart as well, now reading their passwords from the secret. The new version of a workload can also wait several minutes with is not allowed to reveal the secret in get-deployments, while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
  5. Confirm Debezium’s slot is a failover slot: pgx N psql -h 127.0.0.1 -d postgres -c "SELECT slot_name, failover FROM pg_replication_slots" on the leader shows failover = t. If it shows f (1.0.4’s Debezium created it), stop the connector, convert the slot from any psql that reaches the current leader (the slot must be inactive), then start the connector again:
The bundled components move too: PostgreSQL Highly Available 2.5.0 → 2.8.0, Kafka 4.0.1 → 4.3.0 (the brokers roll, which also applies the new super user), and Kafbat UI keeps the setting your release runs with.

Upgrading from 1.1.1 or earlier

1.1.2 moves the database to controlplanecorporation/patroni-postgres:0.8, which carries pgvector 0.8.7 (CVE-2026-103484). The database members restart and nothing limits that rollout, so expect a brief interruption of writes while the leader changes. The upgrade does not redeploy Debezium; it restarts once when the database leader changes and resumes from its failover replication slot, so changes around the leader change may be delivered twice. Upgrade in a quiet window. Existing data needs nothing. If you pinned postgres-highly-available.image, set it to the 0.8 tag: tags 0.1 to 0.7 are refused at render. The pipeline does not use the vector extension; if you created it in your database, run ALTER EXTENSION vector UPDATE; there, which only updates the version \dx reports.

Rotating the Database Password in HA Mode

The HA database needs its own order. The replication role repl shares the password, and a member that restarts with the old password can be promoted to leader, which splits the cluster. Move the leader to member -0 first, so it restarts last, after every other member already has the new password. pgx runs a command in one member (LOCATION is your GVC’s location, DB_USERNAME the secret’s username):
Straight after step 3, put the new password in the secret as BOTH password and sourcePassword, confirm it, and restart the members:
Once the redeployment has finished, confirm one leader and two streaming replicas on one timeline (repeat step 1); -0 restarts last, so the leader moves once more, to another member. For a release upgraded from 1.0.4 that skipped "authentication":null in its upgrade, also remove the install-time password Patroni kept in its cluster configuration:
The procedure moves the leader twice. Debezium’s replication slot is a failover slot that every replica keeps a synchronized copy of, so it resumes on each new leader from where it stopped; changes around a leader change can be delivered twice, and the database drops connections briefly at each leader change. Debezium fails to authenticate from the ALTER ROLE until you have edited the secret: its container then restarts by itself and reads the new sourcePassword, so it needs no redeploy of its own. If it still logs password authentication failed a few minutes after the edit, run cpln workload force-redeployment RELEASE_NAME-debezium --gvc GVC_NAME. Any helm upgrade after the secret has been edited restarts RELEASE_NAME-postgres-ha, RELEASE_NAME-cluster and RELEASE_NAME-debezium again (one more rolling restart of the database, with its leader changes), even when no value changed, because they are rolled whenever the secret they read has changed since the last upgrade. Plan the next upgrade into the same window. If a member stays starting, or running without streaming for several minutes, re-copy it from the leader. This copies the whole database, so it costs network traffic in proportion to its size:

Rotating the Kafka Passwords

All four Kafka passwords live only in the credentials secret, so a rotation is a secret edit and two redeployments, with no helm upgrade. The brokers and Debezium keep the passwords they started with until they restart, so run the steps back to back: a broker that restarts on its own in between comes back with passwords its peers do not have yet.
The brokers restart one at a time. When kafkaInterBrokerPassword or kafkaControllerPassword changed, a restarted broker cannot rejoin the brokers still on the old passwords, so partway through the roll Kafka refuses writes until the last broker has restarted; rotate those two in a quiet window. Debezium is refused from step 3 until step 4, then resumes from its replication slot, so nothing is lost and events around the switch can be delivered twice. Any other client that logs in as admin or debezium needs the new password too.

Scaling and Availability

  • The source database runs three Patroni members behind HAProxy and fails over automatically when the leader is lost; Debezium connects through HAProxy, which routes to the current leader, and its startup step creates the replication slot.
  • Kafka runs three brokers with a replication factor of 3. kafka.kafka.replicas accepts 1, 3, 4 or 5; see Kafka for the rules.
  • Debezium Server is a single replica and cannot be scaled out.
  • Keep postgres-highly-available.etcd.replicas odd, and choose it at install time.

Troubleshooting

Cause: in version 1.0.4 this is expected with the defaults, for up to three reasons. The bundled PostgreSQL cluster runs with wal_level = replica, which pgoutput logical decoding cannot use; database.walLevel is validated but not applied. Kafka enforces ACLs with allowEveryoneIfNoAclFound: false, and the debezium user is neither a super user nor granted any ACL.Fix: read the connector log, filtered server-side, to see which applies:
Adding User:debezium to kafka.kafka.acl.superUsers puts it in the brokers’ super-user list. Patroni keeps wal_level in its cluster-wide configuration, so it is not changed by any value of this chart. Neither change has been verified end to end on this template.
Cause: the secret named in kafka.kafbat_ui.configuration_secret did not exist when the workload was created. The container never starts, so there is no log output.Fix: read status.versions[].message from cpln workload get-deployments RELEASE_NAME-kafbat-ui --gvc GVC_NAME -o yaml to see which secret is missing, create it as shown in Prerequisites, then wait a few minutes or run cpln workload force-redeployment RELEASE_NAME-kafbat-ui --gvc GVC_NAME.
Cause: postgres-highly-available.config.credentialsSecretName, debezium-server.credentialsSecretName and the four kafka.kafka.env entries do not all name one secret.Fix: set all six to the name of your credentials secret, as shown under Database Credentials.
Cause: your values still carry the 1.0.4 credential keys (database:, debezium-server.source.database.password, debezium-server.sink.kafka.saslPassword) or a plain Kafka password.Fix: follow Upgrading from 1.0.4.
Cause: debezium-server.sink.kafka.saslUsername does not appear in kafka.kafka.listeners.client.sasl.users.Fix: add the user to the Kafka list, or set saslUsername to a user already in it.
Cause: postgres-highly-available.walLevel was set to something other than logical.Fix: leave it at logical.
Cause: the credentials secret is named my-cdc-pipeline-credentials by default, without the release name, and secrets are org-wide.Fix: give every release in the org its own credentials secret, and set all six values that name it.
Cause: a replication slot is not advancing — the connector stopped reading, or a slot was left behind after debezium-server.source.postgres.slotName changed. PostgreSQL keeps WAL for an inactive slot indefinitely.Fix: drop the abandoned slot on the source, using its name:

Important Notes

  • Never turn postgres-highly-available.multiZone, postgres-highly-available.etcd.multiZone or kafka.kafka.multiZone off once it has been on. Each replica’s volume stays in the zone it was created in, so with multi-zone off a replica whose volume is in another zone cannot be scheduled and the rollout stops at it. Turning one on later is safe. cpln helm upgrade does not reuse earlier values, so keep the setting in your values file on every upgrade. To recover, upgrade again with it on, then force a redeployment of the affected workload.
  • In version 1.0.4 the pipeline does not deliver change events with its defaults; see Troubleshooting and verify events arrive before relying on it.
  • Create the credentials secret before installing; a missing secret wedges the deployment silently, and only status.versions[].message from cpln workload get-deployments names it.
  • All six values that name the credentials secret must name the same one; the chart refuses anything else.
  • Rotating the database password in HA mode needs a leader switchover first; follow Rotating the Database Password in HA Mode. A plain redeploy can promote a member still holding the old password and split the cluster.
  • Rotating a Kafka password needs both redeployments; see Rotating the Kafka Passwords.
  • This template runs no Kafka exporter, health sidecar, REST Proxy or Kafka Connect, because each needs a password inline; the chart refuses them.
  • kafka.kafka.secrets.kraft_cluster_id is fixed once the brokers have started.
  • Kafbat UI is open to the internet by default; set a strong console login or narrow kafka.kafbat_ui.firewall.external_inboundAllowCIDR.
  • Give each release its own credentials secret; the default name is shared org-wide.
  • Uninstalling deletes every volume set — the source database, the Kafka topics and the Debezium offsets; final snapshots are kept for seven days.
  • Component versions are pinned in this chart (PostgreSQL Highly Available 2.5.0, Kafka 4.0.1, Debezium Server 1.1.1) and change only with a new CDC Pipeline version.

External References