Kafkacat Produce message from a file with headers
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
kafkacat, now commonly referred to as kcat, is excellent for quick Kafka debugging and ad hoc message production. Producing records from a file is easy, and attaching headers is easy too, but only when you are clear about whether the same headers should apply to every record or each line needs different metadata.
That distinction matters because the normal -H flag is invocation-wide. It is a good fit for shared headers, but not a full structured file format for per-message header variation.
Producing One Message Per Line
The basic file-driven pattern is line-based production with -P and -l:
If messages.txt contains:
then each line becomes one produced message value.
The older executable name kafkacat still appears in many examples, but current packages and documentation usually use kcat.
Adding the Same Headers to Every Produced Record
If all records from that command should share the same headers, pass repeated -H flags:
This attaches source=test-run and format=json to every record produced during that invocation. For testing, replaying a fixture, or sending a batch with shared metadata, this is usually all you need.
What -l Does and Does Not Mean
The -l option is only about line-delimited message values. It does not automatically interpret each line as a complex header-plus-body structure.
That is the source of a lot of confusion. Developers often assume a file such as this will somehow drive both headers and payload:
Out of the box, kcat does not infer that format for you. Unless you add your own preprocessing, the file is just message data.
When Each Message Needs Different Headers
If every record needs its own headers, the simplest reliable pattern is to define your own input format and call kcat once per record.
For example, suppose the input file is tab-delimited:
Then a shell loop can split the file and attach the correct header for each line:
This is less compact than a single -l invocation, but it is explicit and easy to reason about. Each line produces one record, and the header value is derived from that same line.
Verifying the Result
Whenever headers matter, consume the records back and inspect them. Producing blindly is not enough, because a command can succeed while still sending the wrong metadata.
A simple verification loop might look like this:
The exact output format can be adjusted, but the important part is checking that both the payload and headers match what you intended to send.
When a Script Is Better Than a One-Liner
If the file format becomes complicated, kcat may stop being the right abstraction. It is still a great tool for quick operational work, but once each record has its own keys, headers, timestamps, or validation rules, a small producer script in Python, Go, or Java is often easier to maintain.
That does not mean kcat is weak. It just means shell pipelines have a complexity limit.
Common Pitfalls
The most common mistake is assuming -l implies a built-in per-line header format. In normal use, it only means one message value per line.
Another mistake is using -H and expecting each header to apply only to one record in a multi-line batch. Repeated -H flags apply to the produced records of that command invocation.
Developers also forget to verify the produced output. A successful command tells you only that records were sent, not that the headers were correct.
Finally, be aware of naming drift. Many older posts say kafkacat, while current installations often expose kcat. The behavior is conceptually the same, but the command name may differ.
Summary
- Use
-Pwith-l fileto produce one message per line from a file. - Use repeated
-H key=valueflags when all messages should share the same headers. - If each message needs different headers, preprocess the file and invoke
kcatper record. - Consume the records back to verify that both payloads and headers are correct.
- Treat
kafkacatas the historical name andkcatas the modern command name.
Related reading
- Kafka/Confluent ProducerConfig change default max.request.size using docker images
- KafkaConsumer position() vs committed()?
- KafkaConsumer `seekToEnd()` does not make consumer consume from latest offset
- KafkaConsumer.close() Why?
- KafkaConsumer.commitAsync() behavior with a lower offset than previous
- kafka.consumer.SimpleConsumer Reconnect due to socket error java.nio.channels.ClosedChannelException
- kafka.errors.KafkaTimeoutError KafkaTimeoutError Failed to update metadata after 60.0 secs
- KafkaException jdk.internal.loader.ClassLoaders can’t find org.apache.kafka.common.security.plain.PlainLoginModule

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.