Designing a PostgreSQL-to-Kafka Change Data Capture Pipeline with Debezium on Kubernetes: Avro Schema Evolution, Snapshotting Strategies, and Exactly-Once Semantics

User avatar placeholder
Written by Tamzid Ahmed

October 20, 2026

Building a reliable PostgreSQL Kafka CDC Debezium Kubernetes pipeline from PostgreSQL to Kafka is a cornerstone of modern event‑driven architectures. This guide walks through deploying Debezium on Kubernetes, handling Avro schema evolution, choosing snapshotting strategies, and guaranteeing exactly‑once semantics.

PostgreSQL Kafka CDC Debezium Kubernetes Architecture

Change Data Capture (CDC) streams row‑level changes from a source database into an event log without polling. Debezium implements this by reading the PostgreSQL logical replication slot and publishing each change as a Kafka record. The pipeline consists of three core components: the PostgreSQL source, the Debezium connector running inside Kafka Connect, and the Kafka cluster that stores the change events.

Core Components

  • PostgreSQL logical replication – provides a stable, ordered stream of INSERT, UPDATE, and DELETE operations.
  • Debezium PostgreSQL connector – transforms the replication stream into structured Avro records.
  • Kafka Connect on Kubernetes – manages connector lifecycle, scaling, and fault tolerance.

Deploying Debezium on Kubernetes

Running Debezium on Kubernetes is most reliable with the Strimzi Kafka Operator and the Debezium Container Images. The following steps produce a production‑grade deployment:

  1. Install the Strimzi operator and create a Kafka custom resource with at least three broker pods.
  2. Deploy a KafkaConnect resource that references the Debezium image debezium/connect:2.5 and mounts a ConfigMap containing the connector configuration.
  3. Apply a KafkaConnector custom resource that defines the PostgreSQL hostname, database name, replication slot, and publication.
  4. Enable the schema.history.internal.kafka.topic and schema.history.internal.kafka.bootstrap.servers settings so schema history survives pod restarts.

Using the operator ensures rolling upgrades, automatic rebalancing, and integrated health checks.

Configuring Avro Schema Evolution

Avro schemas travel with each record, but downstream consumers need a contract that tolerates change. Register schemas in the Confluent Schema Registry (or Apicurio) and set the connector property value.converter to io.confluent.connect.avro.AvroConverter.

Evolution Rules

  • Backward compatibility – new fields must have defaults; consumers built on older schemas can read new records.
  • Forward compatibility – removing fields is safe only if downstream never requires them.
  • Full compatibility – combine both rules for maximum flexibility.

Enforce the desired compatibility level on the registry (e.g., BACKWARD) and configure the connector with schema.compatibility=BACKWARD to reject breaking changes at registration time.

Snapshotting Strategies for Consistency

Debezium offers two snapshotting modes that determine how the initial data load is performed before streaming changes.

Initial Snapshot Modes

  • initial (default) – performs a full table scan once, then switches to streaming. Suitable for tables under a few hundred million rows.
  • never – skips snapshotting; you must bootstrap the topic externally. Use when you have an existing snapshot or want zero‑impact on the source.
  • exported – leverages PostgreSQL’s pg_dump with --format=custom for parallel export, reducing lock time on large tables.

For high‑throughput systems, combine exported with a dedicated read replica to avoid contention on the primary.

Achieving Exactly‑Once Semantics

Exactly‑once delivery across the pipeline requires coordination at three layers: source, connector, and consumer.

Source‑Level Guarantees

PostgreSQL logical replication guarantees that each transaction appears once in the replication stream. Configure the publication with FOR ALL TABLES and enable replica identity FULL on tables lacking a primary key.

Connector‑Level Idempotence

Set transactional.id on the Kafka producer and enable enable.idempotence=true. Debezium emits a __transaction field that downstream consumers can use to deduplicate.

Consumer‑Side Deduplication

Implement a state store (e.g., RocksDB) keyed by the primary key and transaction ID. When a record arrives, commit only if the transaction ID is newer than the stored value.

Operational Best Practices and Monitoring

Observability turns a fragile pipeline into a trustworthy data backbone.

Key Metrics to Watch

  • Connector lag – difference between latest LSN in PostgreSQL and the connector’s processed offset.
  • Schema registry errors – spikes indicate compatibility violations.
  • Kafka Connect task restarts – frequent restarts often signal resource pressure or mis‑configured timeouts.

Alerting Thresholds

Alert when connector lag exceeds 5 minutes, when schema compatibility checks fail, or when any Connect task restarts more than three times per hour.

Conclusion

Designing a PostgreSQL Kafka CDC Debezium Kubernetes pipeline that respects Avro schema evolution, chooses the right snapshotting strategy, and enforces exactly‑once semantics yields a robust foundation for real‑time analytics and microservice integration. Start by deploying the Strimzi operator, register your schemas with backward compatibility, and validate end‑to‑end latency with a synthetic workload before going live.

Leave a Comment