Acks and Durability
The idea in one sentence
Section titled “The idea in one sentence”The acks setting decides how many replicas must confirm a write before Kafka calls it durable — trading latency for the guarantee that your record survives a broker failure.
The three levels of acks
Section titled “The three levels of acks”Every partition has one leader replica and zero or more follower replicas. The acks setting controls how many of them must acknowledge a write before the producer considers send() successful. These three values — 0, 1, and all — are the canonical names used across every Kafka client and the official docs:
acks=0— fire-and-forget. The producer does not wait for any acknowledgement; it counts the record as sent the moment it leaves the client. Lowest latency, but a record can be lost silently if the leader is down.acks=1— leader only. The leader writes the record to its own log and replies. Fast, but if the leader crashes before a follower has replicated that record, the record is lost.acks=all(equivalentlyacks=-1) — every in-sync replica must confirm. The record is safe as long as one in-sync replica survives. Highest durability, highest latency.
# Fire-and-forget: fastest, weakest guaranteeacks=0
# Leader only: fast, but unreplicated writes can be lost on leader failureacks=1
# All in-sync replicas must confirm: strongest durabilityacks=allIn KafkaJS, acks is not a producer-level property — it is a per-send option you pass to send(), using the same three values:
// -1 = all (the default), 1 = leader only, 0 = fire-and-forgetawait producer.send({ topic: 'orders', acks: -1, messages })acks=all needs min.insync.replicas to mean anything
Section titled “acks=all needs min.insync.replicas to mean anything”acks=all alone says “all replicas that are currently in sync.” If only the leader is in sync at that moment, “all” is just the leader — and you are effectively back to acks=1. The broker-side min.insync.replicas closes this gap: it sets the minimum number of in-sync replicas that must be available for the write to be accepted at all.
With min.insync.replicas=2 on a topic with replication factor 3, a producer using acks=all needs at least two replicas in sync. If only one is available, the broker rejects the write (a NotEnoughReplicas error) rather than accepting a record that could vanish. That rejection is the point — you would rather fail loudly than lose data quietly.
Putting this together, a durable KafkaJS producer pairs the per-send acks option (or an idempotent producer, which implies acks: -1 internally — the next lesson covers this) with a min.insync.replicas=2 topic:
const producer = kafka.producer({ idempotent: true }) // implies acks: -1 internally
await producer.send({ topic: 'orders', // a topic configured with min.insync.replicas=2 (broker/topic setting) messages: [{ key: 'order-42', value: JSON.stringify({ amount: 19.99 }) }],})flowchart LR p["Producer send()"] --> a0["acks=0: no wait, lowest latency"] p --> a1["acks=1: leader writes and replies"] p --> aa["acks=all: all in-sync replicas confirm"] aa --> misr["'min.insync.replicas=2' must be met or write is rejected"] misr --> safe["Durable: survives a broker failure"]
The tradeoff, stated plainly
Section titled “The tradeoff, stated plainly”There is no free durability. Each stronger acks level adds a network round trip and waits on more machines, so latency rises as the guarantee strengthens. Pick by the cost of losing a record: metrics and logs can often live with acks=1, but orders, payments, and anything you cannot recreate should use acks=all with min.insync.replicas=2.