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.leveltoread_committedso 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.
A source connector reads from an external system and writes records into Kafka. Exactly-once source support changes how the Connect worker stores its work: the worker writes the produced records and the source offsets inside Kafka transactions, so either both become visible or neither does. This removes the gap where a crash could publish records without recording their offsets, or record offsets for data that never arrived in the topic. The guarantee covers Kafka. It does not extend to any system the source reads from or any system a downstream sink writes to unless those systems support the same commit semantics.
- Worker setting: Enable exactly-once source support in the distributed worker configuration, which requires changes to brokers and workers.
- Cluster upgrades: Plan the rollout as two rolling upgrades on existing clusters, since the feature depends on broker-side changes and worker-side changes that must be sequenced.
- Additional ACLs: Grant the Connect principal the transactional permissions it needs to write offsets and records in transactions, and verify them before enabling the feature.
- Connector support: Confirm per connector whether it implements exactly-once source semantics. Not every source connector can use the feature.
- Source system limits: Confirm whether the source system can replay from a stored position, since the offsets Connect commits must point to a readable point in that system.
Before relying on the feature, check the committed offsets and topic contents after a forced worker restart on a test cluster.
A pipeline is only as strong as its weakest hop, so the guarantee must be assessed across the whole path. Exactly-once inside Kafka does not carry over to the systems on either end. A source's exactly-once behavior ends where Kafka ends, and a sink's behavior ends at the destination's write semantics. Teams often assume the Kafka settings are the whole answer and find duplicates or gaps only after the destination is involved. Mapping each hop and its commit semantics is the first design task.
- Hop inventory: List every system between origin and final storage, and note the commit semantics of each one.
- Transactional boundary: Identify which hops share a transaction or idempotent key, and which only offer at-least-once behavior.
- Intermediate topics: Check whether a stream processor or Single Message Transform stage sits between the connectors, since it adds its own consumer and producer behavior.
- Replay expectations: Define the replay window the business accepts, because it determines how long source positions and topic retention must last.
- Documentation of gaps: Record accepted duplicate or loss risks per hop so auditors and owners see the actual guarantee.
Ask each vendor for its written statement of delivery semantics for the version you run, and keep it with the pipeline design.
Whether a connector can use exactly-once depends on the connector implementation, not on Kafka alone. Some connectors implement the source-side transactional behavior, while many sinks rely on idempotent writes to a destination that supports upserts or keyed overwrites. Because support differs across connectors and versions, the answer must come from each connector's documentation and release notes for the exact build you deploy. Assumptions based on the connector family often prove wrong during testing.
- Source-side support: Check the connector documentation for an explicit statement that it supports exactly-once source semantics.
- Sink-side design: For sinks, check whether the connector offers upsert, primary key or keyed write modes, and whether those modes are on by default.
- Version match: Confirm support for the connector version and the Kafka version in your environment, since both affect the result.
- Licensing and plugins: Check that the connector's license and plugin packaging allow the build you intend to run before you design around its features.
- Vendor confirmation: Where documentation is vague, request written confirmation of exactly-once behavior and its prerequisites from the connector maintainer or vendor.
Record the verified connector name, version and mode in the pipeline design before any build starts.
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.
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.
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.
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.
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.
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.
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.
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.
| Setting | When it fits | Trade-off | What to confirm |
|---|---|---|---|
| Worker-level exactly-once source support | Clusters on Kafka 4.x where source connectors must write transactionally to Kafka | Requires rolling upgrades of brokers and workers before it can be enabled | Worker key name and upgrade order in the Kafka 4.3 Connect documentation |
| Connector-level exactly-once setting | Individual source connectors whose plugin documents exactly-once source support | Connectors without support cannot use it, and per-connector settings add review work | Connector support status and its key names in the connector documentation |
| Sink consumer isolation.level set to read_committed | Sink connectors reading topics written by transactional producers | Consumers wait for open transactions to complete, which adds latency | That the connector passes consumer overrides through to the worker |
| Producer idempotence | Source tasks writing to topics where retries would otherwise create duplicates | Does not stop duplicates that originate in the source system itself | Producer override names and whether the connector exposes them |
| Destination keyed or upsert writes | Sinks writing to databases or lakehouse tables that support keyed commits | Correctness depends on key design and the destination's commit behavior | The sink's documented delivery guarantee and its key or write mode |
| Default at-least-once delivery | Pipelines where duplicates are handled downstream or are acceptable | Least configuration effort, but duplicates are possible after failures | The connector's default delivery guarantee in its documentation |
| Transaction timeout and commit interval | Source connectors with large batches or slow upstream systems | Longer transactions delay visibility and hold resources, while short ones add commit overhead | Timeout 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.

