Skip to content
Apache Kafka kf producers 4 min read

Acks & Durability

The acks producer setting is the single most important knob for write durability in Kafka. It decides how many replica brokers must confirm a record before the producer treats the send as successful, directly trading throughput and latency against the risk of losing data when a broker crashes. Getting it wrong is how teams silently lose messages in production, so it pays to understand exactly what each value waits for and how it combines with min.insync.replicas and the topic’s replication factor.

How acks works

When a producer sends a record, it goes to the leader replica for the target partition. The leader appends the record to its log and, depending on the topic configuration, replicates it to the follower replicas in the in-sync replica (ISR) set. The acks setting controls how long the leader waits before sending an acknowledgement back to the producer.

acksLeader waits forLatencyData-loss window
0Nothing — fire and forgetLowestHigh: record lost if the send fails or the leader is down
1Leader’s own log writeLowMedium: record lost if the leader crashes before followers replicate
all (-1)All in-sync replicas to replicateHighestLowest: record survives as long as one ISR member survives

With acks=0 the producer never knows whether the broker received the record. With acks=1 the leader confirms after writing to its own log but before followers have copied it. With acks=all the leader only confirms once every replica in the current ISR has the record.

Failure scenarios and data-loss windows

The danger with acks=1 is the gap between the leader’s local write and follower replication. Consider replication factor 3:

1. Producer sends record R; leader writes R to its log and returns ack.
2. Producer considers R durable and moves on.
3. Leader crashes BEFORE followers replicate R.
4. A follower that never received R is elected the new leader.
5. R is gone — permanently, with no error ever surfaced to the producer.

acks=all closes this window. The leader will not acknowledge until the followers in the ISR have the record, so a freshly elected leader is guaranteed to already hold it.

Warning: acks=all alone is not enough. If the ISR shrinks to just the leader (all followers fall behind), an “all” ack only means the leader has the data — you are effectively back to acks=1. This is exactly what min.insync.replicas guards against.

Durable writes: acks=all + min.insync.replicas=2 + RF=3

min.insync.replicas (a broker/topic setting, not a producer setting) defines the minimum number of replicas that must be in sync for a write with acks=all to be accepted. If fewer replicas are in sync, the leader rejects the write with NotEnoughReplicasException rather than accepting data it cannot safely replicate.

The canonical durable configuration is:

  • Replication factor = 3 — three copies of every partition.
  • min.insync.replicas = 2 — at least two copies must acknowledge each write.
  • acks = all — the producer waits for those acknowledgements.

This tolerates the loss of one broker with no data loss and no downtime: writes still succeed against the remaining two replicas. If a second broker is lost, writes fail fast (correctly preferring availability of consistency over silently accepting unreplicated data), while reads continue.

RF=3, min.insync.replicas=2, acks=all

  ISR = {leader, follower-1, follower-2}   -> writes OK (3 >= 2)
  one broker down: ISR = {leader, follower} -> writes OK (2 >= 2)
  two brokers down: ISR = {leader}          -> writes REJECTED (1 < 2)

Set min.insync.replicas on the topic so it travels with the data:

kafka-topics.sh --create \
  --bootstrap-server localhost:9092 \
  --topic payments \
  --partitions 6 \
  --replication-factor 3 \
  --config min.insync.replicas=2

Producer configuration

Plain kafka-clients producer tuned for durability:

Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

props.put(ProducerConfig.ACKS_CONFIG, "all");           // wait for all in-sync replicas
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // no duplicates on retry
props.put(ProducerConfig.RETRIES_CONFIG, Integer.MAX_VALUE);
props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 5);

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    RecordMetadata md = producer
        .send(new ProducerRecord<>("payments", "order-42", "{\"amount\":1999}"))
        .get(); // block to surface failures
    System.out.printf("acked at partition %d offset %d%n", md.partition(), md.offset());
}

Output:

acked at partition 3 offset 10472

The same settings in a Spring Boot 3.x application.yaml:

spring:
  kafka:
    producer:
      bootstrap-servers: localhost:9092
      acks: all
      retries: 2147483647
      properties:
        enable.idempotence: true
        max.in.flight.requests.per.connection: 5

Tip: Enabling idempotence implicitly requires acks=all and constrains max.in.flight.requests.per.connection to 5 or fewer. Spring for Apache Kafka and the modern client set these defaults for you, but making them explicit documents your durability intent for the next engineer.

Best Practices

  • Use acks=all for any data you cannot afford to lose (payments, orders, audit events); reserve acks=1 or acks=0 for high-volume, lossy telemetry like metrics or logs.
  • Pair acks=all with min.insync.replicas=2 and replication factor 3 — acks=all is meaningless without an ISR floor.
  • Set min.insync.replicas as a topic config, not a broker default, so the guarantee follows the topic across clusters.
  • Enable the idempotent producer (enable.idempotence=true) so retries from acks=all do not create duplicates.
  • Always inspect the send result (block on Future.get() or use an async callback) — never assume a send succeeded, especially with retries enabled.
  • Keep min.insync.replicas strictly less than the replication factor (2 < 3) so you can lose a broker without halting writes.
Last updated June 1, 2026
Was this helpful?