BlogPostgreSQL

PostgreSQL CDC: how it works

Scott Sternloff

By Scott Sternloff, Senior Enterprise Architect

LinkedIn · Updated · 5 min read

PostgreSQL CDC: how it works
On this page

PostgreSQL change data capture reads row changes from the write-ahead log (WAL) through logical decoding. A replication slot remembers how far a consumer has read, an output plugin turns WAL records into a readable stream, and a client such as Debezium publishes each insert, update and delete as an event. It needs wal_level set to logical, one slot per connector, and a plan for three failures: WAL growth, duplicate events and missing before-images.

This post covers the PostgreSQL side. For the wider choice of CDC methods and tools, see change data capture services. For running connectors in production, see Debezium consulting.

What PostgreSQL needs before a connector connects

  • wal_level = logical. Debezium's PostgreSQL documentation requires it, and the setting is applied at server start, so plan a restart.
  • An output plugin. pgoutput is built in from PostgreSQL 10 and needs no extra libraries. decoderbufs is a community plugin that must be installed. Managed services such as Amazon RDS and Cloud SQL require pgoutput to be named explicitly.
  • A replication role for the connector, with only the privileges it needs.
  • One slot per connector. A slot streams the changes of a single database, and only one receiver can read it at a time, so give each connector its own slot name.
  • A publication. pgoutput reads through publications. Debezium can create one for all tables, which is its default, or a filtered one for the tables you name.
  • Primary keys. Update and delete events identify the row by its replica identity. See below.

PostgreSQL logical replication, Debezium or a managed service

OptionWhat you getFits whenWatch for
Native logical replicationPublications and subscriptions copy tables between PostgreSQL instances.The target is another PostgreSQL and you want no broker.No event stream for other consumers. Schema changes are not replicated for you.
DebeziumOne event per row change, to Kafka Connect topics or, through Debezium Server, to RabbitMQ and other sinks.Services or queues need the changes in commit order.You run the connector, its offsets and the slot.
Managed CDC serviceCapture run by a cloud provider or SaaS, loading a target.The consumer is a warehouse and you do not want to run capture.Pricing model and which targets are supported.

Debezium alternatives compared covers the vendors in more detail.

WAL growth: the failure that fills the disk

A slot keeps WAL until its consumer confirms it. PostgreSQL's documentation warns that neither required WAL nor required catalog rows can be removed as long as a slot needs them, that this can consume significant storage, and that in extreme cases it can force a shutdown to prevent transaction ID wraparound. A slot that is no longer needed should be dropped.

Two things make it worse. First, max_slot_wal_keep_size defaults to -1, which lets slots retain unlimited WAL, so set a limit that protects the primary. A slot that falls further behind than the limit can no longer be used to continue, and the connector then needs a new snapshot. PostgreSQL 18 adds idle_replication_slot_timeout, which is disabled by default (PostgreSQL 18 release notes). Second, slots work per database. If the connector watches a quiet table while other databases or tables on the server are busy, Debezium has nothing to confirm and the slot lags. Debezium's remedy is a heartbeat: set heartbeat.interval.ms, and use heartbeat.action.query to write a row so the position advances.

Alert on retained WAL per slot, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn), and on a confirmed_flush_lsn that stops advancing while the primary keeps writing. A restart_lsn that trails a rising confirmed_flush_lsn is normal: Debezium notes that PostgreSQL reclaims WAL in batches. If you use a heartbeat table, add it to the publication unless the publication is FOR ALL TABLES. A worked case is in the replication slot WAL use case.

Duplicates: PostgreSQL CDC is at least once

The slot position is persisted only at checkpoint, so after a crash the slot can return to an earlier position and send recent changes again. Debezium also commits offsets periodically, so an outage can replay events that consumers have already seen. Neither is a fault. Treat delivery as at least once and make consumers idempotent on the primary key, for example by upserting on key and ignoring an event older than the stored version.

Updates, deletes and replica identity

What an update or delete event contains depends on the table's replica identity. With the default, the previous values include only the primary key, and a table with no primary key emits only create events. REPLICA IDENTITY FULL sends the previous values of every column, at the cost of larger WAL records. Large unchanged TOAST values are left out of update events under the default identity, and Debezium substitutes a placeholder string that is set with unavailable.value.placeholder. Decide per table what consumers need, and add primary keys before you start.

Snapshots and failover

A new connector takes a snapshot first. The default snapshot.mode is initial, and incremental snapshots let you backfill a table added later without stopping streaming, with the read-only variant needing PostgreSQL 13 or later.

Slots live on the primary. From PostgreSQL 17, failover slots let a standby carry a copy, and Debezium can continue reading from the slot on the new primary once slot.failover is enabled and the standby's slot is listed in synchronized_standby_slots. Without that, a promoted standby generally has no slot, so a failover means recreating the slot and snapshotting again. Rehearse it before it happens.

When to get help

AceMQ's Debezium support engineers handle slot and WAL incidents, snapshots and connector restarts, and our PostgreSQL support covers the database side. The CDC design work is described on the change data capture page.

Sources

Documentation read on 31 October 2026. Check the pages for your PostgreSQL and Debezium versions before changing a production system.

Frequently Asked Questions

Does PostgreSQL support CDC?

Yes, through logical decoding. PostgreSQL decodes its write-ahead log into row changes, and a replication slot holds each consumer's position. You need wal_level set to logical. Native logical replication, Debezium and managed services all read changes this way.

Is PostgreSQL CDC exactly once?

No. A slot's position is persisted only at checkpoint, so after a crash recent changes can be sent again, and Debezium commits offsets periodically. Delivery is at least once. Make consumers idempotent on the primary key.

Why is my PostgreSQL WAL growing with a replication slot?

A slot keeps WAL until its consumer confirms it. A stopped connector, a slow consumer, or a connector watching low-traffic tables, or one that shares a server with busy databases, can all leave the slot behind. Monitor slot lag, use heartbeats, and cap retention with max_slot_wal_keep_size.

Do I need Kafka for PostgreSQL CDC?

No. Debezium runs on Kafka Connect, but Debezium Server runs the same PostgreSQL connector as a standalone application and can publish to RabbitMQ and other destinations. Native logical replication needs no broker at all when the target is another PostgreSQL.

Which tool should I use for PostgreSQL CDC?

Use native logical replication to copy PostgreSQL to PostgreSQL, Debezium when services need change events, and a managed service when the consumer is a warehouse and you want someone else to run capture.

Scott Sternloff

Author

Scott SternloffSenior Enterprise Architect

Senior Enterprise Architect at AceMQ. Writes the technical coverage here: RabbitMQ, Kafka and Redis architecture, clustering and migrations.

Talk to an engineer

Need a hand with PostgreSQL?

Senior engineers who run it in production. Get a quote or a free consultation, no sales pitch.

Free Consultation

Get Expert Eyes on Your PostgreSQL Environment

Whether you're troubleshooting a production incident, planning a migration, or want a second opinion on your architecture — our team is ready. No pitch, just answers.

Email Us