A Kafka producer appends records to topic partitions. It is the write side of the system, and its configuration decides three things the rest of your pipeline inherits: how fast records move, whether any are lost or duplicated, and what the cluster lets it touch. The read side lives on the consumer page; the cluster context is in the hub.
The modern client makes the safe path the easy path. With Kafka clients above 3.2.0, an idempotent producing application is straightforward to set up, and the biggest performance win needs no architecture change at all: compression combined with sensible batching.
Producers also have a security footprint worth knowing before the firewall ticket bounces. A write-only producer needs WRITE permission on its specific topics and DESCRIBE permission on the cluster for metadata requests, and nothing more. Transactions are the one feature to reach for only when the job demands it: transactional producers belong where you produce or consume across multiple topics, not in every service by default.
Producers fail bigger than consumers, because a producer failure is upstream of everything. The documented PagerDuty incident is my favourite proof: a single API quirk in the pekko-connectors-kafka library created 4.2 million new producers per hour, 84 times the normal rate, exhausted JVM heap across the cluster and rejected roughly 95% of incoming events for 38 minutes. Nothing in Kafka broke. A client library held the pen. That is why the client choice and lifecycle questions at the end of this page are not an appendix, and why one long-lived producer per application, reused, is the pattern everything else on this page assumes.
Configuration and tuning
The durability decision comes first. acks=all makes the partition leader wait for every in-sync replica before acknowledging a write, and acks=1 acknowledges on the leader alone, trading durability for latency. Everything else is tuned around that choice.
Batching is governed by two parameters working together: batch.size caps the byte size of a single batch, and linger.ms caps how long the producer waits to fill one before sending. A lower linger.ms burns more CPU and network connections for less throughput, while a higher one buys throughput at the cost of latency. Compression rides on batching, with five options: none, gzip, snappy, lz4 and zstd, and it pays twice, on the network and on broker disk.
Standard production tuning for small-to-medium messages is concrete: batch.size=131072 (128 KB), linger.ms=20, compression.type=lz4, buffer.memory=67108864 (64 MB), with topic-level compression.type=producer and min.insync.replicas=2. That is a starting point, not a monument: batching dynamics shift when topics change, and after a topic migration the producer parameters linger.ms, batch.size and buffer.memory need re-tuning.
My linger.ms rule: 5 milliseconds for most use cases, and reserve zero for topics that genuinely need low latency, because zero is not a neutral default, it costs CPU and network connections on both sides of the wire. What you buy by going the other way is documented in numbers. Pushing batch.size to 200000 with linger.ms=100, lz4 and acks=1 took an 8 KB record workload to 94.89 MB/s at 4.92 ms average latency, a 4x throughput increase over the untuned baseline. And DoorDash cut broker CPU 30 to 40% on a high-volume null-keyed ingestion pipeline by raising linger.ms to 50-100 ms. Batching is broker relief, not just client throughput.
Know what acks costs before you blame the network: with acks=all, request-latency-avg typically runs two to five times higher than acks=1 in a perfectly healthy cluster. That multiple is the price of durability working, not a problem to fix. Since Kafka 3.0 acks=all is the default, so the question in a latency review is not “why is this producer slow”, it is “did we mean to pay for durability here”, and the answer is usually yes.
Reliability and delivery guarantees
Idempotence is the producer’s core reliability mechanism, and it is now the default: enable.idempotence=true has been the default since Kafka 3.0 under KIP-679, fully effective from 3.2.0 after a client bug that silently disabled it in some 3.0 and 3.1 configurations. An idempotent producer requires acks=all and retries greater than zero, and Kafka enforces both automatically when idempotence is enabled, so a retry after a transient failure re-delivers the batch without duplicating it.
The in-flight constraint is part of the same guarantee. From Kafka 3.0 the client automatically enforces max.in.flight.requests.per.connection at 5 or lower when idempotence is explicitly enabled. Earlier versions do not, and on those versions a higher in-flight count with retries can reorder writes.
One misconception needs killing here, because it shapes real designs: transactional producers do not eliminate all duplicates. They prevent duplicates within a batch, and a consumer that reads committed data can still see repeats across the boundaries the transaction does not cover. End-to-end exactly-once is a property of the whole pipeline, not a producer flag.
Error handling divides into two classes. Retriable exceptions, such as a leader not yet being available, resolve themselves and are what retries and retry.backoff.ms exist for, with delivery.timeout.ms bounding the total time a record may spend trying. A fatal exception, an oversized record or an authorization failure, will never succeed on retry and must surface to the application instead of spinning.
The idempotence ceiling is worth a concrete warning, because the failure reads like corruption. The guarantee holds up to five in-flight requests per partition, and there is a documented incident of an idempotent producer under higher in-flight load getting OutOfOrderSequenceException back from the broker and surfacing it as a delivery failure. Nothing was corrupt. The producer exceeded the window the sequence-number guarantee covers. Keep in-flight at five or below with idempotence and the whole class disappears.
The broker-side story I tell about producer error handling is my disk-failure incident, because it shows producer symptoms with no producer cause. A single disk failed on a broker where the replica fetcher and IO thread counts were misconfigured, and during recovery the disk could not handle the IOPS of aggressive replica fetching. Producer errors and timeout exceptions hit multiple teams at once. Every team debugged their producer configs. The fix was broker-side. When several producers fail together, stop reading client stack traces and look at the broker they share. And the target that makes all of this discipline worth it is published: DoorDash holds a 99.99% delivery guarantee at their volume, which is what acks, idempotence and bounded retries add up to when nobody freelances.
Common reference targets
For exact property names and default values, the official Apache Kafka documentation is the reference, and defaults move between versions, so a tutorial’s copy of them is stale the day it is published. Give each producer instance a meaningful client.id: it is how metrics aggregate per instance, and an anonymous producer is invisible at exactly the moment you need to find it.
The client library decision is quieter than it looks and matters more. On the JVM, the official Apache Kafka client is the reference implementation. Anywhere else, the reliable choices are clients built as thin wrappers around librdkafka. Clients that reimplement the protocol independently cause quiet, compounding problems through differences in protocol interpretation, the kind of bug that presents as anything except a client bug.
Wrapping the official clients is a legitimate pattern at scale: Netflix runs custom smart clients that wrap the standard producer and consumer interfaces to route writes and reads to the right clusters. Wrapping adds routing and policy on top of the reference implementation. Replacing the implementation is where the trouble starts.
I keep client library choice on my quick-win checklist, next to offset defaults and retention sizing, because it is a one-line decision with cluster-wide blast radius. Open-source clients that implement the Kafka protocol independently are susceptible to subtle errors that manifest as quiet problems and build up until they affect the entire cluster. Quiet is the operative word: a protocol-interpretation bug does not throw, it drifts, and by the time it is visible it looks like a broker problem, a network problem, anything but the library that shipped eighteen months ago. Official client, or a thin librdkafka wrapper, and treat anything else as a risk you are choosing on purpose.
FAQ
What does a producer do in Kafka?
A Kafka producer appends records to topic partitions. It serializes each record, chooses a partition (by key hash or round robin), batches records per partition, compresses the batches, and sends them to the partition leaders, retrying on transient failure. Its configuration decides the delivery guarantee: with acks=all, retries and idempotence, a record is written exactly once to its partition or the producer tells you it failed. Everything downstream inherits those choices.