Preventing duplicate messages with the Kafka idempotent producer starts with knowing when retries duplicate records
The Kafka idempotent producer prevents duplicate records that producer retries create on a partition during a single producer session. The typical failure is simple: the broker writes a record, the acknowledgment is lost on the network or times out, and the producer sends the same record again. Without protection, the partition holds two copies, and every downstream system that reads it sees both. Idempotence is a producer and broker mechanism, so it removes this specific cause of duplication. It does not remove duplicates that start in the application, such as a business event published twice after a user submits a form twice.
Teams usually need this when a duplicate has a cost: a double charge, a second shipment, a repeated alert, or a metric that is summed incorrectly. Start by inventorying every producer and its client library, then record whether idempotence is enabled, which acks setting it uses, and whether retries are on. Retries are common in client defaults, so a producer that nobody has reviewed may already retry without duplicate protection. Apache Kafka is licensed under Apache-2.0 with no paid edition from the project, so turning this on is a configuration and design decision for your team. Confirm the defaults for your client library version in its documentation before you rely on them.
Treat the idempotent producer as one layer. It covers the producer-to-broker hop within one session, so a producer restart can still write a new copy of a record it sent before the restart. Consumers that process messages at least once should detect repeated events, for example by storing a unique event identifier and checking it before applying side effects. Where a read, process and write sequence must land exactly once, transactions are the next step, covered in the sibling exactly-once section. Your design review should name which layer owns each duplicate scenario and who tests it.
Producer settings that enable, depend on or silently disable idempotence
This table compares the producer settings that switch idempotence on, depend on it, or quietly turn it off, with the conflict each one can cause. Read each row against your client version and the 4.3 producer configuration reference, because defaults and validation behavior differ between clients. The main takeaway is that idempotence only protects retries inside one producer session, so the settings around it decide whether duplicates are prevented.
| Setting | Role and documented behavior | What conflicts or disables it | When it fits |
|---|---|---|---|
| enable.idempotence | Assigns the producer a session ID and sequence numbers so the broker drops duplicate retried batches per partition. | Setting it to false removes deduplication. Confirm the default for your client version in the 4.3 reference. | Keep it on for any producer where a duplicate record would change data. |
| acks | Sets how many replicas must confirm a write. Idempotence depends on acknowledgment from all in-sync replicas. | Lowering acks with idempotence enabled can raise a configuration error or disable the guarantee, depending on the client. | Use all, paired with a topic-level min.insync.replicas value that matches your durability target. |
| retries | Controls how many times a failed batch is resent before the send fails. | A low retry count gives up on transient broker errors, which pushes resends back to application code. | Set a high retry budget so transient leader changes recover inside the producer. |
| delivery.timeout.ms | Bounds the total time a send can spend in retries and in-flight waits before it fails. | A short timeout ends sends while retries are still pending. The application may then resend, and that resend sits outside idempotence. | Set it to cover normal leader election time in your cluster, and confirm the value with your own failover tests. |
| max.in.flight.requests.per.connection | Sets how many unacknowledged requests a connection can hold. Idempotence keeps order only up to a limit that depends on the client. | Values above the documented limit for your client can raise a configuration error or let batches arrive out of order. | Keep it at or below the documented limit when record order within a partition matters. |
| Application-level resend | Your code resends after an exception or timeout, which the producer may not recognize as the same send. | After a restart, a new producer session has no link to earlier sends, so an application resend can create a duplicate. | Use it only with an idempotency key in the record or a transactional producer. |
| transactional.id | Enables transactions across partitions and offsets. Transactions require idempotence. | Setting it while enable.idempotence is false produces a configuration error in clients that validate it. | Use it when a read-process-write step must commit output and offsets together. |
| Broker: min.insync.replicas and unclean.leader.election.enable | Control how many replicas must hold a write and whether an out-of-sync replica can become leader. | Unclean leader election can lose acknowledged records, and idempotence cannot restore records the cluster lost. | Keep unclean leader election off for topics where loss is unacceptable, and confirm the setting in your broker configuration. |
How to enable and verify the idempotent producer
This sequence moves a Kafka producer from default settings to a verified configuration that does not write duplicate records when the network or brokers force retries. Each step ends with a check you can record as evidence.
Confirm client and broker versions
Check that your brokers run one of the supported releases listed on the Kafka downloads page: 4.3.1, 4.2.2 or 4.1.2. Confirm that brokers run on Java 17 and that every client and Kafka Streams application runs on Java 11 or later, since Kafka 4.0 set these requirements. Also confirm the client library version matches a release your team can support. The check is a written inventory of broker versions, JVM versions and client library versions, reviewed by the platform owner. Note any cluster still on an older release, because the upgrade notes recommend moving KRaft clusters older than 3.3.x to 3.9.x before moving to 4.x.
Verify replication and min.insync.replicas
Idempotence depends on the broker keeping acknowledged writes, so review the replication factor of each topic the producer writes to and the min.insync.replicas value that applies to it. Use the topic description tooling your team already runs to read these settings. Confirm that the replication factor leaves room for a broker to fail during a write and that min.insync.replicas is set so acks=all produces a meaningful guarantee. The check is a recorded list of topics with their replication factor and min.insync.replicas values, and a note of any topic that does not meet your standard.
Set the idempotent producer configuration
Set enable.idempotence=true in the producer configuration and confirm the client does not override it. Keep acks=all and keep retries enabled with a value high enough that transient errors do not end a send. Do not set max.in.flight.requests.per.connection above the limit the client documents for idempotent producers, and do not set transactional.id unless you also need transactions. Watch for settings that silently disable idempotence or conflict with it, such as an acks value other than all or a configuration override layer that reverts the flag. The check is a startup log or configuration dump that shows the effective value of enable.idempotence and acks for each producer.
Run a fault-injection test that forces retries
In a non-production environment that matches your production settings, make the producer retry on purpose. Options include stopping a broker leader during a send burst, adding network latency or dropping connections between the producer and a broker with a fault tool your team already uses. Send a known number of records with unique keys or sequence numbers while the fault is active. The check is a test record showing that the producer logged retries or resends, and that the fault was active during the send window. A test that produces no retries proves nothing, so repeat the injection until retries appear in the producer metrics.
Check the consumer for duplicates
Read the topic with a consumer that uses read_committed isolation if the producer also uses transactions, and otherwise with a standard consumer. Count the records written and compare them with the records read, then check for repeated unique keys or sequence numbers. Confirm that the count matches exactly. If duplicates appear, look first at the producer configuration, then at whether the failure happened after the broker committed the write but before the producer received the acknowledgment. The check is a reconciliation report with the expected count, the observed count and a list of any duplicate identifiers.
Record the evidence and repeat after upgrades
Save the version inventory, the replication settings, the effective producer configuration, the fault-injection results and the reconciliation report together in your change record. Assign an owner to rerun the fault test after each broker or client upgrade and after any change to topic replication or producer overrides. The check is an approved change record with a named owner and a dated re-test trigger. Idempotence covers duplicates caused by producer retries to a single partition. It does not remove duplicates from application-level resends or from downstream systems, so keep those controls in your design review.
What ordering and duplicate protection cover in each scenario
This is the simplest case and the one where the producer does the most work. A single producer session writes to a single partition, and the broker tracks each batch by producer ID and sequence number. When a request times out and the client resends it, the broker recognizes the sequence it already committed and does not append the batch again. Within that session, the order in which records were sent is the order in which they are appended, provided in-flight requests stay within the limit the client documents for idempotent producers.
- Duplicates from retries: With
enable.idempotenceset totrue, retried batches from this session are written once. - Ordering: Records keep their send order inside the partition while the in-flight limit for idempotent producers is respected. Confirm that limit for your client library version.
- Acknowledgement settings: Idempotence requires
acksset toall, so a write is confirmed by the in-sync replicas before the producer treats it as sent. - Application responsibility: Handling sends that the producer reports as failed after its delivery timeout, and deciding whether to resend the same key and payload.
Check the producer settings in the running service, since a value overridden in a config file or environment variable changes the result.
Idempotence is tracked per partition, so the duplicate protection applies to each partition the producer writes. The broker keeps sequence state for every partition in the session, which means a retried batch is still written once no matter how many partitions the producer uses. Ordering, however, only exists within a partition. Records sent to different partitions have no defined order relative to each other, and a consumer reading several partitions can see them in any interleaving.
- Per-partition sequences: Each partition has its own sequence state, so retries do not duplicate records in any partition.
- No order across partitions: Consumers cannot rely on the relative timing of writes to different partitions.
- Key-based placement: Records with the same key go to the same partition while the partition count stays the same. Changing the partition count changes that mapping, so plan partition counts before production traffic starts.
- Atomicity across partitions: Idempotence does not make a set of writes to several partitions all-or-nothing. Transactions are needed when consumers must see all of the writes or none of them.
Ask which topics need consistency across partitions and whether the design depends on it before relying on idempotence alone.
Each producer instance has its own session and its own producer ID, unless the application sets a transactional.id. The broker cannot connect a retry from one instance with a resend from another. If two replicas of a service both process the same input and both write the resulting event, Kafka stores both events. Idempotence protects against one instance repeating its own sends, and it does not coordinate work between instances.
- Separate deduplication state: Two instances do not share sequence numbers, so the broker treats their writes as independent.
- No order between instances: Records written by different instances to the same partition have no guaranteed relative order.
- Duplicate business events: Duplicate processing of the same input needs an application-level identifier, such as an event ID carried in the record and checked by consumers or a store.
- Overlapping input assignment: When instances join, leave, or restart, the same input can be handled again by another instance, which produces a second write.
Confirm how the deployment starts, stops and scales instances, and whether the same input can reach two of them at the same time.
Idempotence state lives in the producer session. A restarted process normally gets a new producer ID, so the broker cannot match a request sent before the crash with one sent after it. If the process crashed after the broker wrote a batch but before the acknowledgement arrived, the restarted producer may write that batch again. Setting transactional.id gives the producer a stable identity across restarts and lets the broker fence older instances with the same identity. That identity is the basis for transactional writes, and it changes how the application must call the producer.
- New session, new identity: Without
transactional.id, deduplication does not span a crash. - Uncertain last send: The application cannot know whether the last send before a crash succeeded, so it must treat that send as uncertain.
- Transactional identity: A stable
transactional.idenables fencing and requires the application to begin, commit and abort transactions. - Replay markers: Store the source position, such as the input offset, with the output so a replay can be recognized.
Test a forced kill during sends in a staging environment and count the records that arrive downstream.
The idempotent producer only governs what reaches a topic. A consumer that processes a record, writes to a database or calls an external service, and then fails before it commits its offset will receive the record again after restart. Duplicate records that the producer removed never reach the topic, but reprocessing of records that already reached it still happens. Duplicate side effects are therefore a consumer design question, and the producer setting does not answer it.
- Redelivery after failure: A consumer restarts from its last committed offset and rereads anything processed after that point.
- External side effects: Emails, payments, and database writes run again unless the handler checks whether the work is already done.
- Commit timing: When offsets are committed sets the size of the reprocessing window.
- Idempotent handlers: Use unique event identifiers, upserts keyed by business identifiers, or transactions that include the consumed offsets.
Ask each consumer team what it writes outside Kafka and whether that write can be repeated safely.
What idempotence does not cover
The idempotent producer only removes duplicates that the broker can recognize, namely retried sends from one producer session to one partition. The cards below cover the duplicates it cannot see, and each points to the sibling topic that handles that gap.
Application resends with new keys
If application code catches a failed send and submits the same business event again, the producer sees a new request with a new sequence and writes it. Idempotence cannot match records by business meaning. Check whether your service generates an event ID before the first send and reuses it on every retry. For a read-process-write flow that must avoid duplicates end to end, see the sibling topic on exactly-once processing with Kafka transactions.
Consumer reprocessing after commit failures
The producer guarantee ends at the broker log. A consumer that processes a record, then fails before committing its offset, will read that record again after a restart or rebalance. Check whether your handlers are safe to run twice and where offsets are committed relative to side effects. The sibling topic on Kafka consumer offset commit strategies covers commit timing and the trade-offs of each approach.
Writes to external systems
When a consumer writes to a database, search index, payment API or email service, Kafka's guarantees stop at that system's boundary. A timeout after the external write succeeds can cause a retry that duplicates the effect. Confirm whether the target supports idempotent upserts or request keys, and use a unique business key for each write. Transactions alone do not cover writes outside Kafka.
Producer restarts without transactional IDs
Idempotence protects one producer session. When a producer process restarts, it gets a new producer ID unless it uses a transactional ID, so records it resends after a crash can be written again. Check how your deployment restarts producers and whether any upstream retry logic replays data from a checkpoint. Transactional producers, with stable transactional IDs and fencing of old instances, are covered in the sibling topic on exactly-once processing.
Duplicates from upstream sources
A source system, change data capture connector or webhook may emit the same change more than once, for example after its own retry or a failover. The producer receives these as distinct inputs and cannot tell they are copies. Ask each source whether it provides a stable event identifier and whether replays are possible. Your pipeline must carry that identifier into the record so downstream steps can recognize it.
Deduplication across a stream or topic
Removing duplicates from a stream over a time window or across topics requires state that outlives a single producer session. Idempotence does not provide this, and a compacted topic only keeps the latest value per key after cleanup runs. For stateful stream processing, see the sibling topics on running Kafka Streams applications in production and on Spark Structured Streaming with Kafka, which covers deduplication in that engine. Confirm the window length and key choice before you build it.
Questions teams ask before enabling the idempotent producer
Does enabling the idempotent producer cost extra?
No license fee applies. Apache Kafka is released under the Apache License 2.0 and the project offers no paid edition, so the cost sits in engineering time, test environments and the infrastructure that runs the cluster. Changing the setting in a client is a small edit. The larger cost is verifying behavior across every producer, including client library versions, retry settings and any code that resends messages on its own. The one-time cost is analysis and testing. The recurring cost is staff time to keep client libraries aligned with broker versions during upgrades. A fixed-scope quote from DeployOpen would itemize these items from a written scope, and your team can ask for the same breakdown from any implementation partner.
How long does a rollout take, and what drives the timeline?
The timeline depends on how many producers you run, how many client languages and library versions they use, and how much test coverage exists before production. A single service with a clear configuration moves quickly. A platform with dozens of teams on mixed clients needs an inventory, a test plan per client and coordinated change windows. Upgrade timing also matters: the project aims for three releases a year, and each supported release has a defined upgrade path, so a client change may be bundled with a broker upgrade. Ask any partner for a schedule tied to your change calendar and your list of producers, not a generic duration.
What does our team need to provide before the work starts?
Start with four inputs:
- The current broker version and a plan to reach a supported release, such as 4.1.2, 4.2.2 or 4.3.1 from the Kafka downloads page. Brokers require Java 17, and clients and Kafka Streams applications require at least Java 11.
- An inventory of topics and their configurations, including replication, retention and any compacted topics.
- A list of producers, their languages, client library versions and any custom retry or resend logic.
- A test environment that mirrors production client behavior, with the ability to inject broker restarts and network faults.
Without these, the team cannot measure whether duplicates have actually stopped, so confirm each input before the scope is signed.
Can our own engineers enable the idempotent producer without a partner?
Yes, for a contained change. A team that already runs Kafka and understands producer acknowledgments and retries can enable the setting in one service, test it and roll it out. The work gets harder when many services use different client libraries, when producers sit in several teams, or when the cluster has access control that may need new permissions. Engineers should read the producer configuration reference for their client and the Kafka documentation for their version before changing defaults. Many organizations use a partner for the first rollout to set the test method and standards, then take over the routine changes.
Does the idempotent producer stop every duplicate message?
No. The idempotent producer addresses duplicates that come from producer retries when a send is repeated after an uncertain response from the broker. It does not remove duplicates caused by application code that resends a message after its own timeout, by consumers that reprocess records after a failed offset commit, or by side effects in external systems that the consumer writes to. Those cases need idempotent handling in the application, such as unique keys in the target store, plus careful offset commit design. Treat the producer setting as one control in a wider design, and test each path that can create a second record.
Does the idempotent producer work with transactions?
Transactions are a separate feature that builds on the same producer behavior, and they add their own configuration, consumer isolation and fencing rules. Enable and test them as a distinct change rather than assuming one covers the other. Confirm that your client library supports both features at the version you run, and check the required settings for your Kafka release in the documentation. The exactly-once processing topic in this series covers read-process-write flows, consumer isolation and the limits of exactly-once with external systems in more detail.
Do authorization settings change when idempotence is enabled?
They can. In KRaft clusters, the StandardAuthorizer stores ACLs in the cluster metadata log, and resources without ACLs are restricted to super users unless allow.everyone.if.no.acl.found is set to true. Producers that write through a secured listener must have the write permissions they already need, and you should confirm in the authorization documentation whether idempotent writes require an additional permission in your setup. Test this on a non-production cluster with the same authorizer configuration, since a missing permission often appears only as a rejected produce request.
When a team should bring in an outside partner
A team should consider an outside partner when idempotence stops being a single producer setting and becomes a property of many producers that the organization must prove works. The idempotent producer, enabled with enable.idempotence, is a client-side mechanism, so its protection depends on every producer that writes to a topic. Partners are most useful when the producer fleet is spread across several languages, such as Java services alongside librdkafka-based Python or Go clients, because each client library has its own defaults, version alignment rules and way of exposing retry and acknowledgment settings. Checking that each library enables idempotence, sets acks=all and keeps retries on, and that no team overrides these keys in a shared configuration file, is a bounded inventory job that a partner can run against a written list of applications.
A second group of cases involves regulated audit trails for message delivery. An auditor may ask how a payment or claim event was written exactly once, and the answer must be backed by configuration evidence, test results and retained records, not by a developer's recollection. Idempotence covers duplicates caused by producer retries within a session and partition. It does not cover duplicates created by an application that resubmits a business request after a timeout, and it does not cover consumer-side reprocessing. A partner can help define where the boundary sits, document which controls cover which failure, and prepare the evidence set. DeployOpen prepares systems and evidence for compliance assessments, but it does not issue audit reports or attestations, and the independent assessor's scope should be confirmed before work starts.
DeployOpen works under a fixed-scope written agreement that names the producer applications, client libraries, Kafka versions and topics in scope, along with the deliverables and timeline agreed before work begins. Typical work includes a configuration review of each producer, fault-injection tests that force broker failover, network timeouts and producer restarts while checking for duplicate or out-of-order records, and written runbooks for the producer settings that must not change. Ask any partner to show how it would test these failures in your environment, which Kafka version and client versions it would use, and what records it would hand over. Confirm that the test environment matches production settings for max.in.flight.requests.per.connection and delivery.timeout.ms, because a test that passes on different values proves little.
How DeployOpen scopes and delivers producer hardening for Kafka
DeployOpen approaches duplicate prevention as a producer configuration and verification task inside your own Kafka cluster, not as a change to application logic alone. We start from your existing producers, topics and broker settings, then deliver a written, fixed-scope plan and the tests that show whether retries can still create duplicates.
How we scope
- Inventory every producer client, language library and client version that writes to the cluster, including any that set retries or acks explicitly.
- Record which topics need duplicate protection and which can tolerate at-least-once delivery, with the consumer behavior each choice assumes.
- Agree in writing whether the engagement covers producer configuration only or also the broker, ACL and monitoring work around it.
How we build
- Set idempotence explicitly on each producer, then check that acks, retries and in-flight request limits match the chosen guarantees.
- Remove or correct overrides that silently disable idempotence, and confirm the client library version supports the feature on your brokers.
- Run failure tests that force retries and broker restarts, and compare the topic contents against the number of records the application sent.
How we hand over
- Deliver a configuration reference listing each producer setting, its value, the reason for it and the owning team.
- Document the limits of idempotence, including duplicates across separate producer sessions and from application-level resubmission, with the controls to address them.
- Walk your platform and application teams through the test results and the runbook for checking producer settings during upgrades.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers, so producer settings can be rechecked when clients or brokers change.
- Ask for a retest plan after each Kafka upgrade, since client defaults and broker requirements can change between releases.
- Confirm with DeployOpen during scoping which items, such as monitoring alerts or dashboards for producer errors, are included in support.

