
Beyond streaminglog processing
NATS JetStream and Apache Kafka — the architectural differences that matter, and where each one wins.
- Author
- Jean-Noël Moyne
- Version
- V 2.0, May 2026
- Compares
- NATS 2.12 / Apache Kafka 4.2
Executive Summary
NATS JetStream and Apache Kafka overlap in functionality but differ in architecture. Apache Kafka is a distributed append-only log processing system designed for write throughput; at its core, log persistence and replay is its sole function. To filter, merge, key/value, queue, or replicate across regions, you bolt on Kafka Streams, Flink, MirrorMaker 2, Schema Registry, and Kafka Connect — each as separate JVMs.
NATS consolidates messaging, streaming, Key/Value, Object Store, queue semantics, service discovery, multi-region super-clustering, edge leaf nodes, MQTT, and WebSockets into a single binary under 20MB with zero dependencies. The practical consequences of this architectural difference show up across every dimension compared in this paper.
Kafka 4 has closed some historical gaps, but the architectural difference remains: Kafka's model is a partitioned append-only log extended by external components, while NATS JetStream is a consistent addressable message store with messaging, queuing, and data storage built in.
"Neither system is universally better. The right choice depends on whether you're building log-processing pipelines or unified distributed applications."
Key Takeaways
- Kafka is a partitioned, append-only log; NATS JetStream is a consistent, addressable message store with built-in messaging, queuing, KV, and Object Store.
- Kafka requires a fleet of JVM processes for full functionality; NATS ships everything in a single ~20MB nats-server binary.
- NATS uses subject-based addressing with wildcards and native multi-tenancy through accounts; Kafka relies on topic naming conventions and ACLs.
- Kafka excels at high-throughput log processing with deep Flink/Iceberg/Schema Registry integration.
- NATS JetStream wins on operational simplicity, multi-region super-clusters, edge leaf nodes, GDPR-friendly deletion, and microsecond latency.
- The two systems are not mutually exclusive — they bridge cleanly via Synadia Connect, Debezium Server, or direct protocol adapters.
Components
What you install and operate
NATS and Kafka differ substantially in what you install and operate, though the gap has narrowed with Kafka 4.0.
Runtime and footprint
Kafka is written in Java and Scala and runs on the JVM. NATS is written in Go and ships as a single static binary. Running Kafka means provisioning a JVM, choosing and maintaining a JDK, and tuning heap size, garbage collection, and page cache behavior for each broker. NATS has none of that — the nats-server binary is self-contained, and there is no runtime to tune.
For the artifact sizes, the Apache Kafka release is a compressed archive that expands to roughly 200 files totaling around 100 MB, of which about half are JAR dependencies. The NATS release contains a single nats-server binary on the order of 20 MB along with a README and license file. Administrators and developers typically also install the companion nats CLI — a single binary that covers administration, monitoring, benchmarking, traffic generation, and interactive testing across every NATS feature, in place of the collection of shell scripts Kafka ships for the same purposes.
Embedded NATS: a deployment mode with no Kafka equivalent
If your development language is Go, NATS has one additional deployment mode that has no Kafka equivalent: the server can be embedded directly inside an application binary. This makes it straightforward to ship self-contained distributed applications where every instance runs its own embedded NATS server and the instances form a cluster among themselves — useful for any peer-to-peer distributed use case that needs reliable local messaging without a separate service dependency.
Processes
For most of Kafka's history, running a cluster meant operating two distinct kinds of JVM process: ZooKeeper nodes for cluster metadata and leader election, and broker nodes for data. With the release of Apache Kafka 4.0 in March 2025, ZooKeeper was removed entirely and KRaft (Kafka Raft) is now the only supported mode for metadata management. New Kafka deployments run a single type of JVM process in one of three roles: controller-only, broker-only, or combined.
This is a meaningful simplification and closes a long-standing operational gap. It is not, however, the whole picture.
A production Kafka deployment is rarely just brokers. If you want the stream processing capabilities compared against in the rest of this paper — filtering, joining, aggregating, materialized views, stream-to-stream transformations, connectors to databases and object stores — you run additional JVMs for Kafka Streams applications, Kafka Connect workers, Schema Registry, and increasingly a dedicated stream processing runtime such as Apache Flink alongside. Multi-region topologies require MirrorMaker 2, Cluster Linking, or a third-party replicator. Each of these is a separate set of JVMs with its own configuration, monitoring, and failure modes.
With NATS, everything in the equivalent matrix — clustering, super-clustering across regions, leaf nodes at the edge, stream mirroring, stream sourcing, Key/Value, Object Store, MQTT, WebSockets, service discovery for request-reply — is built into the same nats-server binary. Features are enabled in configuration rather than added by deploying new processes.
Client library parity
NATS JetStream has maintained client libraries for Go, Java, .NET, Python, JavaScript/TypeScript, Rust, C, Ruby, and others. Applications connecting to NATS do not require the language-level (and JVM coupling) that Kafka Streams or Flink imposes.
A note on the broader ecosystem
This paper compares NATS to Apache Kafka specifically. Several Kafka-protocol-compatible alternatives — Redpanda, WarpStream, AutoMQ — address the JVM and single-binary critiques in different ways. The architectural comparisons in the rest of this paper, however, concern the Kafka model itself (topic/partition/consumer-group, append-only log, offset-based addressing) and apply regardless of which Kafka-compatible implementation you run.
Functionality
"Messaging" vs "Streaming"
Kafka is designed for distributed log processing, and at its core (without needing to use Flink or Kafka Streams) that is its sole function. You can do basic publish/subscribe to topics using Kafka, but when it comes to proper low-latency messaging functionality it is not comparable to NATS. At its core, NATS is a dedicated high-speed distributed messaging system designed for low latency, used extensively in real-time environments where latencies have to be measured in microseconds or milliseconds. NATS was designed at Apcera as the core nervous system of a modern PaaS.
Apache Kafka
Distributed append-only log optimized for throughput. Core function is log persistence and replay.
- Designed for batch and micro-batch workloads
- Latencies measured in tens of milliseconds
- Requires external components for filtering and transformation
NATS JetStream
High-speed messaging system with streaming capabilities built in.
- Designed for real-time messaging
- Latencies measured in microseconds
- Subject-based filtering and transformation built in
Messages
In NATS, messages are fundamentally addressed: every message is published to a subject (a hierarchical name like orders.us.east), and subscribers express interest by subscribing to subjects or subject patterns. The server routes messages based on subject matching. This is true whether or not persistence is involved — core NATS messaging and JetStream both use the same subject-based addressing model.
In Kafka, messages are not addressed. Messages are appended to a specific partition of a specific topic. The only identifier is the offset within that partition. Consumers read from partitions by offset, not by any property of the message itself. To route a message to specific consumers, you either use distinct topics or rely on consumer-side filtering.
"Subjects" vs "Topics"
A NATS subject is a hierarchical dot-separated string (e.g. orders.region.us) that supports wildcard matching: * matches a single token, > matches one or more tokens. A JetStream stream captures messages from one or more subjects (or subject patterns) and stores them durably. Consumers can subscribe to a subset of those subjects within the stream.
A Kafka topic is a durable log divided into partitions. There is no hierarchy, no wildcard subscription at the broker level. Topic filtering or content-based routing requires either multiple topics or a consumer-side filter.
Streaming functionality
JetStream provides streaming on top of NATS subjects. A stream is configured with one or more subject filters (which can include wildcards); any message published to a matching subject is captured and persisted. Streams support configurable retention policies (by count, age, size), deduplication, and sequence-based or time-based replay.
Kafka topics are inherently durable logs with configurable retention. The model is simpler but less flexible: retention is per-topic, not per-message, and there is no native subject-based filtering within a topic.
Qualities of Service
NATS JetStream supports multiple acknowledgment modes: at-most-once (publish without waiting for ack), at-least-once (publish with ack, consumer ack required), and exactly-once semantics through message deduplication at the stream level and double-ack confirmation for consumers. Deduplication is enabled by the Nats-Msg-Id header and a configurable deduplication window.
Kafka's exactly-once semantics require enabling idempotent producers and transactional APIs. The transactional model is powerful but operationally complex: it requires a transaction coordinator, careful producer configuration, and consumer-side read_committed isolation.
Data Store: JetStream persistence enables more than just Streams
JetStream persistence is the foundation for two additional built-in data stores:
Key/Value buckets
A KV bucket is a stream configured with per-key retention (last value wins). Keys are dot-separated strings, supporting wildcard watches. You can watch all keys (>), a subset (users.>), or a single key. History is available if configured.
Object Store
For large objects that exceed the message size limit, Object Store chunks data into multiple messages and manages reassembly. Metadata is stored in a companion KV bucket. The interface is get/put/delete by object name.
Kafka has no native Key/Value or Object Store abstraction. KTables in Kafka Streams provide compacted key-based state, but they are maintained by the Streams application, not the broker. Large object storage typically involves external systems (S3, GCS) with Kafka carrying references or metadata.
Queuing
In NATS, queue semantics are first-class. A queue group is a named set of subscribers; within a group, each message is delivered to exactly one subscriber (load-balanced). This applies to both core NATS messaging and JetStream consumers. No additional process is required.
In Kafka, queue-like semantics are achieved through consumer groups. A topic's partitions are distributed among consumers in a group; each partition is assigned to exactly one consumer. This provides parallelism but ties scaling to partition count — you cannot have more active consumers than partitions. Rebalancing on group membership changes can cause latency spikes.
Cross-cluster behavior
NATS supports multi-region deployments natively through super-clusters (clusters connected via gateways) and leaf nodes (lightweight edge connections). Messages route transparently across cluster boundaries based on subject interest. Stream mirroring and sourcing provide async replication with subject filtering and transformation.
Kafka multi-region deployments typically use MirrorMaker 2 or Confluent's Cluster Linking. MirrorMaker 2 runs as Kafka Connect workers and requires separate configuration and monitoring. Cluster Linking (Confluent Platform) provides tighter integration but is a licensed feature.
Microservices vs CQRS
NATS includes built-in service discovery for request-reply patterns. A service advertises itself by subscribing to a subject; the nats CLI can discover, describe, and benchmark services without external tooling. This makes NATS suitable for both async messaging and synchronous microservice communication.
Kafka is fundamentally asynchronous. Request-reply patterns require application-level correlation IDs and temporary topics. Service discovery requires external mechanisms (DNS, service mesh, Kubernetes services).
Durability
Both systems support configurable replication factors. JetStream streams and KV buckets can be configured with N replicas using Raft consensus. Kafka partitions are replicated across brokers; the producer can require acknowledgment from all in-sync replicas before considering a write committed.
Protocol
Wire-level differences
NATS uses a simple text-based protocol (with optional binary payloads) over TCP. The protocol is human-readable, easy to debug with standard tools, and supported across 40+ client libraries. WebSocket and MQTT are also supported natively.
Kafka uses a custom binary protocol. While efficient, it requires Kafka-specific client libraries. The protocol has evolved over many versions, and client-broker compatibility requires version negotiation.
"A protocol you can debug with
telnetis a protocol you can reason about under pressure."
Security
Boundaries, identity, and compliance
Security models diverge sharply between the two systems — especially around tenancy and the ability to delete individual messages for compliance.
Authentication
NATS supports token, username/password, TLS certificates, NKeys (ed25519 public-key crypto), and decentralized JWTs. JWT-based auth enables self-service account management without server restarts. Kafka supports SASL (PLAIN, SCRAM, GSSAPI/Kerberos, OAUTHBEARER) and TLS client certs; ACL changes typically require restarts.
Multi-tenancy
NATS has native multi-tenancy through accounts — isolated namespaces with their own subjects, streams, and KV buckets, plus per-account resource limits. Kafka multi-tenancy is typically achieved through topic naming conventions and ACLs; there is no native account isolation.
Encryption
Both systems support TLS for encryption in transit. NATS JetStream additionally supports at-rest encryption for stored messages.
Message deletion & GDPR
NATS JetStream supports direct deletion by sequence number or stream purge with a subject filter — critical for GDPR. Kafka is an append-only log: the only options are waiting for retention, log compaction (with tombstones), or recreating topics. GDPR compliance in Kafka typically requires crypto-shredding.
Layer 7 firewalling
NATS's text-based protocol is easier to inspect and filter at the application layer. Standard L7 proxies can understand and route NATS traffic.
Deployment and Operations
From single cluster to multi-region edge
Resilience
NATS clusters form a full mesh; any server can accept any client connection. JetStream streams are replicated using Raft consensus across a configurable number of servers. Leader election is automatic and transparent.
Kafka brokers are coordinated by KRaft controllers (or ZooKeeper in older versions). Partition leaders are elected among in-sync replicas. The controller manages cluster membership and partition assignments.
Clustering
A NATS cluster is a full mesh of nats-server instances. Configuration is a list of peer addresses. Scaling up means adding servers to the cluster list and reloading configuration.
A Kafka cluster requires running brokers with matching cluster IDs and either ZooKeeper (deprecated) or KRaft controllers. Scaling requires rebalancing partitions across the new broker.
Super-clusters and Leaf Nodes
NATS super-clusters connect independent clusters via gateways. Interest-based routing means messages only flow between clusters when there are subscribers. Leaf nodes provide lightweight connections for edge deployments or isolated namespaces.
Kafka has no native super-cluster concept. Multi-cluster deployments use MirrorMaker 2 or Cluster Linking. Both require additional configuration and processes.
Data placement
JetStream streams can specify placement constraints: tags for rack/zone awareness, cluster targeting in super-clusters, and explicit replica placement. Stream migration between clusters is a single administrative command.
Kafka partition placement is managed by the controller. Rack awareness is supported but requires broker configuration. Moving partitions requires running a reassignment plan and waiting for data replication.
Operations
Container orchestration, observability, and tracing
Container orchestration
NATS provides official Helm charts for Kubernetes with full JetStream support. The NATS Kubernetes Operator (NACK) manages streams and consumers declaratively via CRDs.
Kafka has multiple Kubernetes operators: Strimzi (open source) and Confluent for Kubernetes (commercial). Both provide CRD-based management.
Observability and monitoring
NATS exposes built-in HTTP endpoints (/varz, /connz, /jsz, etc.) returning JSON for real-time server state. The system account provides push-based event streams for advisories and stats. NATS Surveyor aggregates metrics across clusters for Prometheus scraping.
Kafka metrics are exposed via JMX. Production deployments typically run JMX exporters alongside each broker, feeding Prometheus or similar systems. The metrics surface is large and requires understanding JMX tooling.
Distributed message tracing
NATS 2.11 introduced built-in distributed message tracing. Setting a Nats-Trace-Dest header causes every server on the message path to publish trace events. The nats trace CLI visualizes the routing tree. A dry-run mode traces without delivering.
Kafka requires OpenTelemetry instrumentation in producers and consumers, propagating trace context through message headers. This traces application-level flow but not internal broker routing.
Administration
CLIs, UIs, and day-2 operations
The nats CLI is a single binary for all administrative tasks: stream and consumer management, benchmarking, tracing, account inspection, and more. It works over the NATS protocol without requiring HTTP access to servers.
Kafka administration uses multiple shell scripts (kafka-topics.sh, kafka-consumer-groups.sh, etc.) requiring the Kafka distribution installed locally.
Admin UI
Synadia Control Plane provides a commercial web UI for NATS administration. Several open-source UIs exist for both NATS and Kafka.
Change Data Capture
Connecting databases to streams
Debezium Server supports NATS JetStream as a first-class sink. Change events land directly in JetStream streams with full Debezium event structure, no Kafka Connect required.
Kafka remains the primary Debezium target. The Kafka Connect deployment model provides scaling and fault tolerance but requires additional JVM processes.
What NATS adds to CDC
- Subject-based addressing: Route CDC events by table or schema using subject wildcards.
- Key/Value for materialized views: Built-in KV buckets for maintaining entity state.
Other CDC tools
Tools like Sequin provide Postgres-native CDC with built-in NATS sinks, eliminating even the Debezium dependency for PostgreSQL workloads.
Stream Processing & Summary
Compute layers, platform, and where each one wins
The Kafka-side compute layer has three tiers
Kafka Streams, ksqlDB, and Apache Flink provide stream processing capabilities. All require additional JVM processes. Flink has become the dominant choice for production workloads.
The NATS-side compute layer is thinner by design
Many operations that require Kafka Streams in Kafka are built into the NATS server: subject filtering, stream sourcing with transformation, Key/Value with history, deduplication.
Direct client implementation
Because JetStream capabilities are server-side, complex streaming logic can be implemented directly in application code without a separate stream processing framework.
Synadia Platform
Synadia Platform provides Control Plane (management UI), Synadia Connect (data integration), and Synadia Insights (observability and optimization) for NATS deployments.
Flink with NATS
For teams already invested in Flink, NATS connectors are available. NATS can serve as both source and sink for Flink jobs.
KStreams and KTables compared to JetStream primitives
KStreams and KTables are client-side abstractions over Kafka topics. JetStream streams and KV buckets are server-side primitives. The NATS model avoids data shuffling between brokers and stream processors.
Summary
Kafka and NATS JetStream are both capable streaming platforms with different architectural foundations. Kafka excels at high-throughput log processing with a rich ecosystem. NATS JetStream offers operational simplicity, lower latency, and a unified platform for messaging, streaming, and data storage.
"They are not mutually exclusive. Many organizations run both — Kafka for batch analytics and data lake integration, NATS for operational messaging and edge deployments."
The two systems bridge cleanly via Synadia Connect, Debezium Server, or direct protocol adapters, and can coexist cooperatively in the same architecture.
Learn more
Frequently Asked Questions
How is NATS JetStream different from Apache Kafka architecturally?
Apache Kafka is a partitioned, append-only log optimized for write throughput. NATS JetStream is a consistent, addressable message store with subject-based routing, built-in queuing, Key/Value, and Object Store. Kafka relies on offset-based addressing within partitions, while JetStream uses hierarchical subjects with wildcard matching.
Did Kafka 4 close the gap by removing ZooKeeper?
Kafka 4.0 (March 2025) removed ZooKeeper and made KRaft the only metadata-management mode, which is a meaningful operational simplification. But a production Kafka deployment is rarely just brokers — Kafka Streams, Kafka Connect, Schema Registry, Flink, and MirrorMaker 2 are still separate JVM processes. NATS consolidates the equivalent capabilities into a single nats-server binary.
What does "subjects vs topics" mean in practice?
A NATS subject is a dot-separated hierarchical name like orders.region.us that supports wildcard matching (* matches one token, > matches one or more). Subscribers express interest by subject pattern. A Kafka topic has no hierarchy or broker-side wildcard subscription — filtering or content-based routing must use multiple topics or consumer-side filters.
How does NATS handle multi-region deployments compared to Kafka?
NATS supports super-clusters (clusters connected via gateways) and leaf nodes (lightweight edge connections) natively. Messages route across cluster boundaries based on subject interest, and stream mirroring or sourcing handles async replication. Kafka multi-region typically uses MirrorMaker 2 or Confluent's commercial Cluster Linking, both requiring additional configuration and processes.
Can NATS JetStream delete individual messages for GDPR?
Yes. JetStream supports direct message deletion by sequence number or stream purge with a subject filter — critical for GDPR. Kafka is an append-only log: the only options are retention expiry, log compaction with tombstones, or recreating topics. GDPR compliance in Kafka typically requires crypto-shredding (encrypting with per-user keys and deleting the keys).
What does NATS multi-tenancy look like compared to Kafka ACLs?
NATS has native multi-tenancy through accounts — each account is an isolated namespace with its own subjects, streams, and KV buckets, with per-account resource limits and selective subject exports. Kafka multi-tenancy is typically achieved through topic naming conventions and ACLs — there is no native account isolation in the open-source broker.
Do I have to choose between NATS and Kafka?
No. The two are not mutually exclusive — they bridge cleanly via Synadia Connect, Debezium Server, or direct protocol adapters. Many organizations run both: Kafka for batch analytics and data-lake integration, NATS for operational messaging, microservices, and edge deployments.
When does Kafka still win?
Kafka's ecosystem depth, transactional model, and throughput optimizations make it the stronger choice for large-scale log processing with deep Flink, Iceberg, or Schema Registry integration. If your workload is primarily batch or micro-batch ETL with established Kafka tooling, Kafka is the practical default.
When is NATS JetStream the better fit?
NATS JetStream's unified architecture, operational simplicity, compliance-friendly deletion, multi-tenant security, microsecond latency, and edge deployment capabilities make it the stronger choice for teams building modern distributed applications that span messaging, streaming, and data storage in a single platform — especially for real-time, multi-region, or edge-heavy workloads.
