Exactly-once delivery in Kafka Connect

Exactly-once in Kafka Connect depends on which side of the pipeline you are on, how your cluster is upgraded and which connectors support it. This page covers each decision and the work to confirm it.

On this page

Exactly-once in Kafka Connect: what it covers and when you need it

Exactly-once delivery in Kafka Connect covers two separate paths, and most teams only need to fix one of them. If a pipeline must neither duplicate nor drop records in a downstream system, first decide which side of the pipeline carries that requirement. The sink side reads from Kafka and writes to a database, search index or lakehouse. Here the work is shared between the consumer and the connector: the consumer reads with the read_committed isolation level so it skips records from aborted transactions, and the connector's writes must be idempotent, for example upserts keyed on a stable primary key. Apache Kafka is licensed under Apache 2.0, and the behavior described here reflects the Kafka 4.3 documentation, so confirm your version against it before you design around it.

The source side reads from an external system and writes into Kafka. Exactly-once source support is a worker-level capability, so it depends on the Connect cluster, the brokers and the security setup working together. Enabling it changes how the worker coordinates offsets and transactions, which means the cluster must run a version that supports it and the worker's principal needs the additional ACLs that transactional writes require. Those changes reach into the broker and the authorization model, not only the connector configuration, so they belong in a change plan with a rollback path. Check the security overview and the authorization documentation for the exact permissions your authorizer needs before you grant anything, and confirm the source connector's own support for this mode.

Decide per connector and per destination before committing. Confirm in each connector's documentation whether it supports exactly-once source semantics and whether its sink writes are idempotent for your key design, because a transport guarantee does not fix a connector that appends duplicates on retry. Also check whether the downstream system can deduplicate on a stable key as a fallback. If the destination cannot be made idempotent and the source cannot yet be enabled safely, plan for at-least-once delivery with deduplication in the target, and record that decision for the team that will own the pipeline. The Apache Kafka 4.3 upgrade notes are the next read for version prerequisites.

Sink and source exactly-once: which side you are fixing

A sink connector reads topics from Kafka and writes to an external system. On the Kafka side, the consumer can be set to read only committed transactional records, which means records from aborted or still open transactions are skipped. This prevents duplicates or partial data from upstream transactional producers from reaching the sink. The destination is a separate question. Kafka cannot make an external write atomic with the consumer offset, so the sink must also be idempotent (writes with stable keys that overwrite rather than append) or transactional in its own right.

  • Consumer isolation: Set the Connect worker or connector consumer isolation.level to read_committed so only committed transactional records are read.
  • Upstream producers: Confirm that the producers writing these topics use transactions. Read_committed has no effect on records written without a transaction.
  • Idempotent writes: Use stable record keys and an upsert or overwrite write mode in the sink, so a replayed batch produces the same final state.
  • Offset commit timing: Understand that a crash between the destination write and the offset commit can replay records, which is why the destination must tolerate replays.
  • Destination semantics: Confirm with the destination vendor whether its bulk or batch APIs support atomic commits or deduplication.

Check the sink's delivery behavior with a replay test that kills a worker mid-batch and compares final destination state with the source topic.

Which connectors can actually use exactly-once

Exactly-once in Connect depends on the connector, not only on the worker. A connector can use it only when its source can commit offsets together with the records it produces, and its sink can write without duplicating data on retry. Check each connector against the same conditions before you plan a pipeline around them.

Source offsets must commit atomically

An exactly-once source connector has to write its records and its source position in one Kafka transaction, so a restart resumes from exactly the last committed point. This only works if the source system exposes a position the connector can store and replay, such as a log sequence or a change stream token. Ask the connector maintainer which source positions it stores, and whether a restart can rewind to a point that was already published.

Sinks must be idempotent or transactional

A sink that retries a failed write can duplicate rows or documents unless it either uses stable record keys with upserts or participates in a transaction. Downstream systems that accept only append writes need extra design, such as deduplication keys or a staging table. Confirm how the sink builds its keys and what happens to a retried batch that partly succeeded.

Connector must declare support

The connector has to advertise exactly-once support in its documentation or its release notes for the version you run. Support is a property of a specific connector release, so a version upgrade can add or remove it. Record the connector name, version and the support statement in your pipeline design so that the claim can be checked during review.

Kafka docs do not vouch for connectors

The Kafka documentation describes what the framework supports, but it does not guarantee that every third-party connector meets the same semantics. Read the connector's own documentation and test its failure behavior with a restart during a write, not only on the happy path. Treat any connector without a written guarantee as at-least-once until your tests show otherwise.

Verify with failure tests

Test each connector with forced worker restarts, broker failovers and duplicate source events, then compare sink output with the source. Count rows or documents, check key uniqueness and confirm that consumers reading with read_committed see no aborted records. Keep the test results with the pipeline design so the reasoning survives staff changes.

Setting up exactly-once source support on an existing cluster

This sequence moves an existing Kafka cluster and its Connect workers to a state where source connectors can write with exactly-once guarantees. Each step closes with a check so the team knows the cluster is ready before the next change.

  1. Confirm broker and Connect worker versions

    Inventory every broker and every Connect worker, recording the Kafka release and the Java runtime on each node. Brokers, Connect and tools require Java 17 from Kafka 4.0 onward, so workers on older JVMs must be upgraded before anything else. Confirm the cluster is on a supported release from the downloads page, and check that the version reported by each broker matches your inventory. The step is done when every node reports a supported release and Java 17 is confirmed on every worker.

  2. Plan the two rolling upgrades

    Kafka's time-based release plan supports rolling upgrades from each of the last three releases to the latest version. If the cluster is older than that window, schedule intermediate upgrades first and follow the upgrade notes for each hop. Enabling exactly-once source support on existing workers takes two rolling restarts: one that puts workers into the preparing state and one that moves them to enabled. Write both restart windows, the rollback point for each, and the owner of each node in the change record. The step is done when the plan is approved and each node has a named owner.

  3. Add transactional ACLs for the Connect principal

    Source tasks that use exactly-once write through transactions, so the Connect worker principal needs permission to use the transactional IDs it generates and idempotent writes at the cluster level. In KRaft clusters the StandardAuthorizer stores these ACLs in the metadata log, and any resource without an ACL is limited to super users unless allow.everyone.if.no.acl.found is set to true. Confirm the exact resource types and prefixes against the authorization documentation for your release before you apply them. The step is done when the ACL list shows the entries and a test principal can start a transaction.

  4. Apply the preparing setting in the first rolling restart

    Set the worker property exactly.once.source.support to preparing in each worker configuration, then restart the workers one at a time so the group stays available. Check that the worker reports the new value in its configuration and logs, and that tasks reassign after each restart. Keep connectors running on the existing delivery mode during this phase. Confirm the exact property name and the allowed values in the Connect configuration reference for your release. The step is done when every worker has restarted on preparing and all connectors show RUNNING.

  5. Switch workers to enabled in the second restart

    Once every worker is on preparing, change exactly.once.source.support to enabled and perform the second rolling restart in the same node order. This second pass is what allows connectors to request exactly-once delivery, so do not start it while any worker is still on the older setting. After each restart, read the worker configuration and confirm the value. The step is done when all workers report enabled, the worker group is stable, and no tasks are stuck in a rebalance loop.

  6. Enable exactly-once on each source connector

    For each source connector that should use exactly-once, set its connector-level exactly-once setting and restart the connector. Run the change on a non-production connector first, and keep a copy of the previous configuration so the change can be reverted. Connectors that cannot support exactly-once will reject the setting or report it as unsupported, so record the result for each connector. The step is done when the connector status endpoint shows the connector and all of its tasks in the RUNNING state.

  7. Verify delivery with read_committed consumers

    Configure downstream consumers and sink connectors to read with isolation.level set to read_committed, so they only see records from committed transactions. Then test failure handling by restarting a worker during a steady load and comparing record counts and keys in the target against the source. Record how long recovery takes in your own environment and keep the test result with the change record. The step is done when the target shows no duplicate keys after the restart and the test results are filed for your auditors or platform owners.

Configuration options and when each fits

This table compares the settings that shape exactly-once behavior in Connect, from worker and connector configuration on the source side to consumer and producer settings on the sink side. Read each row for when it fits and what it costs in latency, upgrade effort or destination dependence. The main takeaway is that exactly-once depends on the whole path, so enable it only where both the connector and the destination support it.

SettingWhen it fitsTrade-offWhat to confirm
Worker-level exactly-once source supportClusters on Kafka 4.x where source connectors must write transactionally to KafkaRequires rolling upgrades of brokers and workers before it can be enabledWorker key name and upgrade order in the Kafka 4.3 Connect documentation
Connector-level exactly-once settingIndividual source connectors whose plugin documents exactly-once source supportConnectors without support cannot use it, and per-connector settings add review workConnector support status and its key names in the connector documentation
Sink consumer isolation.level set to read_committedSink connectors reading topics written by transactional producersConsumers wait for open transactions to complete, which adds latencyThat the connector passes consumer overrides through to the worker
Producer idempotenceSource tasks writing to topics where retries would otherwise create duplicatesDoes not stop duplicates that originate in the source system itselfProducer override names and whether the connector exposes them
Destination keyed or upsert writesSinks writing to databases or lakehouse tables that support keyed commitsCorrectness depends on key design and the destination's commit behaviorThe sink's documented delivery guarantee and its key or write mode
Default at-least-once deliveryPipelines where duplicates are handled downstream or are acceptableLeast configuration effort, but duplicates are possible after failuresThe connector's default delivery guarantee in its documentation
Transaction timeout and commit intervalSource connectors with large batches or slow upstream systemsLonger transactions delay visibility and hold resources, while short ones add commit overheadTimeout key names and broker transaction limits for your Kafka version

Extra ACLs and access control for transactional Connect

Exactly-once source support needs write permissions that a Connect cluster may not have needed before. Each source task writes through a transactional producer, so the broker authorizes the worker's transactional IDs as well as the topics it writes to. In KRaft clusters, the StandardAuthorizer stores these ACLs in the cluster metadata log, and any resource without an ACL is limited to super users unless allow.everyone.if.no.acl.found is set to true. Before you enable the feature, list every transactional ID pattern the workers will use for your Connect version and grant WRITE and DESCRIBE on that TransactionalId resource to the worker principal. Confirm the exact operations and ID patterns against the Kafka authorization documentation for your release.

The worker also needs access to its own internal state. Grant the worker principal the topic operations it requires on the topics named by config.storage.topic, offset.storage.topic and status.storage.topic, plus group access for group.id. Source connectors need write access to their destination topics, and sink connectors need read access to their input topics and their consumer groups, since sinks reading with read_committed depend on those grants as much as on the transaction settings. Keep allow.everyone.if.no.acl.found set to false. If it is enabled, any resource you forget to grant becomes open to every authenticated principal, which hides gaps instead of exposing them during testing.

The worker's identity is what the ACLs check, so the connection setup matters. The Kafka security overview documents SSL and SASL options for client connections, including the SASL mechanisms the project supports. Use a dedicated principal for each Connect cluster so its grants can be reviewed and revoked on their own, and avoid sharing one principal across unrelated pipelines. Before production, run the worker with a test principal that lacks the new grants and confirm which tasks fail and with what error, then add the grants one at a time with the kafka-acls tool and record each change. Review the ACL list again after every connector addition, because each new transactional source adds entries of its own.

Operating exactly-once pipelines: monitoring and failure modes

An exactly-once pipeline is only as reliable as the monitoring around it. The cards below cover the signals to watch, the failure modes that affect transactional writes, and the tests to run before a connector carries production traffic.

Monitoring connector and task status

Watch the state of every connector and task through the Connect REST API and your metrics stack, not just the connector's overall status. A task stuck in FAILED or a connector paused by an operator can look healthy at the topic level while consumption stops. Alert on task failures and on lag between the source offsets and the topic end offsets. Confirm your alerts fire when a task fails, since that is the first signal of a stalled sink.

Transaction timeouts and aborted transactions

Transactional producers and sinks that read with read_committed depend on transactions committing within their timeout. When a transaction times out or is aborted, its records stay invisible to read_committed consumers, and downstream counts drop without an error on the sink. Track aborted transaction rates on the broker side and compare sink row counts with source counts over time. Ask your team to define which timeout values apply to each connector and who owns changes to them.

Worker restarts and rebalances

Restarting a Connect worker or adding one triggers a rebalance that reassigns tasks. Exactly-once guarantees hold across this only when producer fencing and offset commits are working as designed, so a rebalance during a long transaction is a case to test. Schedule worker restarts one at a time, watch for repeated rebalances in the logs, and confirm that tasks resume from the last committed offsets. Frequent rebalances usually point to unstable workers or session timeouts that are too short.

Duplicate and replay scenarios

Before production, replay a known input window through the pipeline and compare the output with an expected result. Include cases such as killing a task mid-batch, restarting the sink during a transaction and reprocessing from an earlier offset. The aim is to confirm that the destination holds each record once, not to prove that the framework never retries. Document the expected output for each scenario so that the result can be checked by someone other than the author.

Access control changes after upgrades

Exactly-once paths need ACLs for transactional IDs and for the Connect internal topics, and a missing grant often surfaces only after a rolling upgrade or a credential rotation. Review authorization failures in broker logs after each change. Keep a checklist of the ACLs each connector needs so that a new worker does not start with a partial permission set. Confirm the list with your security owner before each rollout.

Sink idempotence as a backstop

Exactly-once delivery reduces duplicates but does not replace idempotent writes in the destination. Where the sink supports upserts or primary keys, use them so that a replay produces the same final state. Review each sink's write mode and key design as part of the operating checklist. Ask the connector vendor or project which failure modes its documentation covers and which remain your responsibility.

Exactly-once in Kafka Connect: questions teams ask before hiring help

Is exactly-once delivery worth the extra complexity in Kafka Connect?

It depends on what a duplicate or a missing record costs at the destination. Connect pipelines deliver at least once by default, so a retry after a failure can write the same record twice unless the sink handles duplicates. Exactly-once support adds transactional settings, extra permissions and a rolling upgrade path, and it only protects the stages it covers. Teams that feed dashboards or analytics often accept idempotent upserts and skip the extra work. Teams that move financial or regulated records usually need the guarantee end to end. Review the destination's write model first, then decide whether the platform changes are justified.

What does exactly-once in Kafka Connect require from our team?

Three groups carry most of the work. Kafka and Connect operators plan the broker and worker upgrades, set the configuration and watch each rollout. ACL owners add the transactional and cluster permissions that the authorizer needs, and they confirm that the service accounts match those grants. Destination owners explain how their system handles writes, keys, deletes and duplicate detection, then test the sink with replayed data. Security and change management approve each window. Without a named owner in each group, the work stalls at the first permission denial.

How long do the upgrades take for our cluster and change windows?

No duration is fixed for this work, so ask for one tied to your environment. The effort depends on the number of brokers and Connect workers, how many clusters are involved, how often your change windows open, and how long validation takes after each restart. Kafka supports rolling upgrades from each release in the past year to the latest version, so the upgrade can proceed in controlled steps. Check the upgrade notes for the exact versions on your path, then confirm the number of windows with your change board. A quote should show the window count and the validation steps for each one.

Can our own team implement exactly-once without outside help?

Often yes, if the team already runs Kafka in production and has done a rolling upgrade on the same version path. The work is mostly configuration, permissions and testing, with little new code. Gaps usually appear in the details: transactional IDs per connector, authorizer rules for each service account, and destination behavior under replay. Small platform teams often find that testing and rollback planning take the most time. Ask who will own the setup after launch, because the upgrade is only the start. Outside help makes the most sense when the change window is tight or the destination has no clear duplicate handling.

What does a fixed-scope quote cover for this work?

A fixed-scope quote covers the work listed in the written scope: the agreed systems, the deliverables and the timelines, all confirmed in writing before work starts. For this topic, the scope should name the clusters, Connect workers and connectors involved, the ACL changes, the upgrade plan with rollback steps, the destination tests and the handover documents. It should also state what you provide, such as access, test environments and named owners, and what sits outside the scope. Ongoing support, later upgrades and continuous testing are available as separate retainers. DeployOpen does not publish a price list, so ask for a quote built from the written scope and check each deliverable against it.

Which connectors should we check first for exactly-once support?

Start with the sinks that write to systems where duplicates are hard to clean up. Check each connector's own documentation for its delivery guarantee, its idempotent or upsert write mode and the key settings it requires, because Kafka does not decide how the destination handles writes. The sibling pages on Iceberg sinks, Debezium JDBC sinks, the MongoDB connector and the OpenSearch sink describe how each one writes and handles failures. Check the license terms before you depend on a connector, and confirm that the plugin version runs on your Connect release.

Does read_committed on a sink give exactly-once by itself?

No. Setting isolation.level to read_committed makes consumers skip records from aborted transactions, which matters when the source side writes transactionally. It does not stop a sink from writing the same record twice to its destination after a retry. The sink still needs idempotent writes, stable keys or a transaction that its destination supports. Test this by forcing a worker restart during a load test, then count the records at the destination and compare them with the source. Document the result, because data owners and auditors will ask how duplicates were ruled out.

What ACLs does exactly-once Kafka Connect need?

The authorizer must allow each Connect service account to use its transactional IDs and to perform the cluster-level operations that idempotent producers need. In KRaft clusters, the StandardAuthorizer stores these rules in the cluster metadata log. Resources without ACLs are limited to super users unless allow.everyone.if.no.acl.found is set to true. Keep that setting off and write explicit grants for each connector identity, then review them with the ACL owners. Confirm the exact operations for your Kafka release in the authorization documentation before changing production.

Limits to confirm in your environment

Exactly-once in Connect depends on more than the framework setting. The cards below cover the version, connector, runtime and replication questions that decide whether the guarantee holds in your environment. Confirm each one with the connector vendor, the Kafka documentation or your own test cluster before you rely on it.

Connector support per version

Exactly-once source support is a framework feature, but each source connector must also implement it. Ask each connector vendor which versions of the connector support exactly-once source semantics, and whether that support depends on a specific Kafka Connect or connector release. A connector that does not implement the feature will run at-least-once even when the worker is configured for exactly-once. Record the answer per connector and per version in your pipeline inventory.

Destination idempotence

Exactly-once across a sink depends on the destination. Some sinks deduplicate by key or upsert on a primary key, while others append and cannot undo a duplicate write. Confirm with each sink vendor whether writes are idempotent for your key design, how retries after a partial failure behave, and whether delete events are applied in order. Where the destination is not idempotent, plan a deduplication step or accept at-least-once semantics for that path.

Java 17 on workers

Since Kafka 4.0, brokers, Kafka Connect and tools require Java 17. Connect workers running older JVMs or custom base images will fail to start or behave unpredictably after an upgrade. Check every worker image and host, including sidecar tooling that bundles its own runtime. Confirm the connector plugins you install also run on the JVM your workers use, because a plugin built for an older Java version can break at load time.

Supported upgrade path to 4.3.1

Exactly-once source support requires two rolling upgrades on an existing cluster, so the starting version determines the route. Rolling upgrades are supported from each release in the past year to the latest version, so confirm your current 4.x release is inside that window. If you run a KRaft cluster older than 3.3.x, the upgrade notes recommend moving to 3.9.x first. Check the release status on the downloads page before you schedule the work.

MirrorMaker 2 interaction

If MirrorMaker 2 replicates topics between clusters, confirm how it interacts with exactly-once delivery on both sides. MirrorMaker 2 is built on Kafka Connect and preserves partitioning, offsets and ACLs, but duplicate or reordered records during failover are a separate concern from the Connect delivery guarantee. Test a failover with transactional consumers on the target cluster, and confirm how offsets translate between clusters before you promise end-to-end exactly-once across sites.

Licensing of third-party connectors

Exactly-once is only useful if you are allowed to run the connector that provides it. Some connectors are published under licenses that restrict production use, bundling or redistribution, and some enterprise connectors gate the feature behind a paid tier. Check the license of each connector and its exact version before you build on it. Record the license, the owner and the terms that govern redistribution inside your images.

How DeployOpen scopes and delivers exactly-once Connect pipelines

DeployOpen plans, implements, hardens and documents self-hosted Kafka Connect pipelines inside your own infrastructure, including the sink settings, source upgrade path and ACLs that exactly-once delivery depends on. Each engagement starts with a written scope that states which connectors, topics and clusters are covered before any configuration changes are made.

How we scope

  • Inventory each connector, its sink or source role, and whether its plugin supports exactly-once semantics.
  • Confirm the Kafka version, Connect worker version and Java runtime on every cluster involved.
  • Record which topics, consumer groups and ACLs the pipeline needs, and who approves changes to them.

How we build

  • Set read_committed on sink consumers and test that uncommitted transactional records are never visible downstream.
  • Plan the two rolling upgrades for existing clusters and the Connect worker settings for exactly-once source support.
  • Add the extra ACLs for transactional producers and Connect internal topics, then verify them in a non-production environment.

How we hand over

  • Deliver configuration, ACL definitions and upgrade runbooks as documented artifacts stored in your repository.
  • Document which connectors are confirmed for exactly-once and which need at-least-once handling with idempotent writes.
  • Walk your operations team through failure tests, including worker restarts and broker failover during transactions.

How we support

  • Ongoing support, upgrades and continuous testing are available as a retainer after handover.
  • Re-verify exactly-once behavior when you change connector versions, Kafka releases or the Connect worker configuration.
  • Confirm each change against the written scope before it reaches production, and update the runbooks to match.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.