# NATS JetStream vs Kafka for Teams That Do Not Need Kafka's Scale

> Kafka is the reflexive answer to "we need a message log," but for workloads under a million messages per second, NATS JetStream gives you durable streams and solid delivery guarantees with a fraction of the operational cost.

URL: https://www.matthewswong.com/en/blog/nats-jetstream-vs-kafka-lightweight/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-07-14
- Updated: 2026-07-14
- Category: Backend

> **Key takeaway:** For teams processing thousands of messages per second rather than millions, NATS JetStream delivers durable streams, push and pull consumers, and at-least-once or exactly-once delivery from a single self-contained binary. Kafka wins at massive throughput and long retention, but its partition planning and operational surface are overkill for most backends.

Every few months a team reaches for Kafka because it is the default answer to "we need a message log." Then they spend a sprint on partition counts, consumer group rebalancing, retention tuning, and wiring up an observability stack before a single business event flows through it. I have watched this happen on services that peak at a few thousand messages per second. That is not Kafka's home turf, and paying Kafka's operational tax to sit in it is a bad trade.

NATS JetStream is the persistence layer built into the NATS server. If you already speak the NATS subject model, you get durable streams, replayable history, and delivery guarantees by flipping one configuration flag. This post walks through streams, consumers, and the operational reality of running each, with a comparison table so you can make the call for your own workload.

## What JetStream actually is

Core NATS is fire-and-forget pub/sub: if no subscriber is listening, the message is gone. JetStream adds a storage layer on top of the same server, so messages published to a subject are captured into a stream and can be replayed on demand. There is no separate process, no separate cluster, and no separate client library. You enable it with one flag and keep the same connection and subject model you already use.

A stream defines what subjects it captures and how it retains data. The three retention policies matter because they map directly to common backend patterns.

- Limits — keep messages until an age, size, or count limit is hit. This is the Kafka-style replayable log.
- Work Queue — a message is deleted once a consumer acknowledges it. Perfect for job queues where each task runs exactly once.
- Interest — messages are kept only while there is an active consumer interested in the subject. A middle ground that avoids unbounded growth.

## Consumers: push, pull, and acknowledgment

A consumer is a stateful view into a stream that tracks which messages a client has seen. Pull consumers are the modern default: the client asks for a batch when it is ready, which gives you natural backpressure and horizontal scaling without ever defining a partition. Multiple workers can share one pull consumer like a queue group, and the server load-balances across them.

Acknowledgment policy is where you tune delivery guarantees. Explicit ack means each message must be confirmed or it will be redelivered after a timeout — this is your at-least-once guarantee. JetStream also offers exactly-once using publisher message IDs for deduplication plus a double-ack on consumption. Contrast that with Kafka, where exactly-once requires transactions and idempotent producers configured correctly across the pipeline.

> Set max_deliver to a finite number and pair the stream with a dead-letter strategy. Without a delivery cap, a message your handler always throws on will be redelivered forever, quietly burning a worker in a loop. This is the JetStream equivalent of a Kafka poison-pill and it bites teams that assume redelivery is free.

## The operational reality

Kafka 4.0, released in March 2025, removed ZooKeeper entirely in favor of KRaft, its own Raft-based metadata layer. That is a genuine simplification — one system instead of two, and clusters that scale to millions of partitions. But KRaft does not remove the parts that actually cost you time: partition planning, consumer group rebalancing, retention and segment tuning, and understanding distributed log semantics before you can debug a lag spike.

JetStream ships as a single Go binary with no JVM to tune and no heap to size. On the VPS I run, a three-node clustered NATS deployment with JetStream enabled fits comfortably in memory that a single Kafka broker would consider an insult. Replication is Raft-based per stream, monitoring is a built-in HTTP endpoint, and the same nats CLI you use to publish also inspects streams and consumers. The whole thing is one mental model.

> Do not read "lightweight" as "unlimited." JetStream is not built for petabyte-scale retention or the vast third-party connector ecosystem Kafka Connect gives you. If you need to fan out into dozens of data warehouses and lakes, or you truly are pushing millions of messages per second with month-long retention, Kafka earns its complexity. Match the tool to the actual load, not the load you imagine.

## Head-to-head comparison

| Dimension | NATS JetStream | Apache Kafka 4.0 |
| --- | --- | --- |
| Deployment | Single Go binary, one flag to enable, no JVM | JVM-based brokers, KRaft controllers, memory sizing |
| Scaling unit | Consumers over subjects, no partition planning | Partitions per topic, planned up front |
| Delivery guarantees | At-least-once by default, exactly-once via msg IDs plus double-ack | At-least-once, exactly-once via transactions and idempotent producers |
| Retention models | Limits, Work Queue, Interest — per stream | Time or size based log retention, plus compaction |
| Peak throughput | Comfortable at thousands to low millions msg/s | Purpose-built for sustained millions of msg/s |
| Ecosystem | Lean, subject-based, edge and multi-tenant friendly | Vast — Kafka Connect, Streams, Schema Registry |

## How I decide

1. Estimate real peak throughput. If it is under a few hundred thousand messages per second, JetStream is almost certainly enough.
2. Check your retention need. Days-to-weeks of replay is fine on JetStream file storage; months of petabyte-scale history leans Kafka.
3. Count your connectors. If you live in Kafka Connect and Schema Registry, staying in that ecosystem is worth more than shaving operational cost.
4. Weigh who operates it. A small team without a dedicated platform engineer will ship faster and sleep better on JetStream.

My rule is simple: reach for Kafka when you can name the specific Kafka feature you need — Connect, sustained massive throughput, or long compacted history. If your honest answer is "I need a durable, replayable message log with good delivery guarantees," JetStream gives you that with a fraction of the operational surface, and you can always graduate later if the load truly arrives.

## Sources

- Apache Kafka 4.0.0 Release Announcement — ZooKeeper removed, KRaft only
- NATS JetStream concepts — streams, consumers, retention, acknowledgments
- NATS and Kafka Compared — Synadia

## Source links

- https://kafka.apache.org/blog/2025/03/18/apache-kafka-4.0.0-release-announcement/
- https://docs.nats.io/nats-concepts/jetstream
- https://www.synadia.com/blog/nats-and-kafka-compared

## FAQ

### Is NATS JetStream a real replacement for Kafka?

For the majority of backends, yes. JetStream provides durable, replayable streams, push and pull consumers, and at-least-once or exactly-once delivery. It is not a replacement when you specifically need Kafka Connect's connector ecosystem, sustained multi-million message-per-second throughput, or petabyte-scale long-term retention.

### How does JetStream achieve exactly-once delivery?

Publishers attach a unique message ID that the server uses to deduplicate messages within a configurable window, preventing duplicate publishes. On the consumer side, a double-acknowledgment confirms the message was both received and processed. Together these give exactly-once semantics without the transaction coordination Kafka requires.

### What is the difference between push and pull consumers in JetStream?

Push consumers have the server deliver messages automatically to a subject, which is simple but offers less flow control. Pull consumers let the client request batches when ready, giving natural backpressure and horizontal scaling. Pull consumers are the recommended default for most durable processing workloads.

### Does Kafka 4.0 still need ZooKeeper?

No. Kafka 4.0, released in March 2025, removed ZooKeeper entirely in favor of KRaft, its own Raft-based metadata layer. This simplifies the architecture to a single system and scales to millions of partitions, but it does not remove partition planning or consumer group tuning.

### How much infrastructure does JetStream need to run?

JetStream is built into the single nats-server Go binary and is enabled with one configuration block or flag. There is no JVM to tune, no separate ZooKeeper or KRaft controller, and monitoring is a built-in HTTP endpoint. A three-node clustered deployment runs comfortably on modest VPS resources.

---

- HTML version: https://www.matthewswong.com/en/blog/nats-jetstream-vs-kafka-lightweight/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/nats-jetstream-vs-kafka-lightweight/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
