What is Nats?
Scroll to explore
Nats
A communication fabric for distributed applications.
Nats lets any service talk to any other service—without knowing where it lives, how many instances are running, or whether it's even online yet. One protocol covers pub/sub, request/reply, queue-based load distribution, persistent streaming, key-value storage, and object storage. Layer on multi-region clustering, edge deployments via leaf nodes, and built-in security with accounts and decentralized auth—all from a single binary.
But to understand what that means—and everything Nats can do—let's start with something everyone knows.
The Http Server
A process listening on a port.
Everyone is familiar with the Http server. It's just a process running on a machine. It binds to a port (usually 80 or 443), accepts connections, and speaks a protocol: Http.
A client connects, sends a request, gets a response. That's the whole model. Under the hood, it's all built on Tcp—Transmission Control Protocol handles the reliable delivery so the application doesn't have to.
This is the mental model to hold onto: a server is just a process, listening on a port, speaking a protocol over Tcp.
Why this matters: Nats is exactly the same idea. A process, a port, a protocol over Tcp. The difference is what happens once you're connected.
It's a Single Binary
learn more →A server that listens on Tcp.
At its core, Nats is a process—just like an Http server. It binds to a Tcp port, accepts connections, and routes messages. Nothing exotic about that.
Where an Http server listens on port 80 and speaks Http, a Nats server listens on port 4222 and speaks the Nats protocol. Clients connect via Tcp, send messages, and receive messages. No magic, no special infrastructure—just a process on your machine.
A simple foundation—a single binary speaking a simple protocol over Tcp.
Why this matters: That's it. A process, a port, a protocol. The same mental model as the Http server you already know—just speaking a different language on the wire.
Why Not Use Http?
The Problem with Http
A server process on Tcp. So why not just use Http?
Http works. But as systems grow, you bolt on a message broker for async work, a service mesh for discovery, a load balancer for routing, and a cache for shared state. Each one adds operational burden, failure modes, and complexity.
The reason starts with what's underneath—Tcp itself—and extends to what Http was designed to do.
The Limits of Http
Client asks. Server answers. That's the whole protocol.
Http does exactly one thing: a client sends a request to a specific server, and the server responds. Everything else—push notifications, streaming, fan-out—is bolted on after the fact.
Why this matters: These aren't missing features—they're the model. Http was designed for documents, not distributed systems. Location dependence, point-to-point coupling, and synchronous blocking are baked into every request. You can work around them, but you're always fighting the protocol.
Still Http
Better protocols don't change the model underneath.
Each one improves something—encoding, query flexibility, full-duplex—but none of them introduce native many-to-many messaging. Every connection is still one client talking to one server.
Http with conventions for resources and verbs. The most common API style, but still one request, one response.
Flexible queries through a single Http endpoint. Subscriptions bolt on WebSockets.
Binary protobuf over Http/2 streams. Supports streaming, but each stream is still between two endpoints.
Upgrades past Http to a full-duplex Tcp channel. Bidirectional, but still one client talking to one server.
Why this matters: Some of these tools add streaming and bidirectional communication, but the topology stays the same: one client, one server. For pub/sub fan-out, queue-based load balancing, and location-transparent routing—you still end up needing a separate system.
Tcp's Trade-offs
Built for reliability. Not for millions of messages per second.
Tcp handles a lot for you—retransmissions, congestion control, ordered delivery. The kernel takes care of it so your application doesn't have to.
But Tcp was designed before anyone imagined moving millions of messages per second. At that scale, its “helpful” features start working against you.
Reliable. Ordered. But not built for millions of messages per second. These aren't bugs—they're trade-offs from a protocol designed before high-throughput messaging existed.
One lost packet stalls everything behind it
Kernel buffers can balloon, adding latency spikes
Tcp keepalives take minutes to detect dead peers
Why this matters: Understanding Tcp's limitations explains why high-throughput messaging systems can't just use raw sockets—they need an application-level protocol that actively manages flow control, detects failures fast, and avoids head-of-line blocking.
How Nats Builds on Tcp
The Nats protocol is human-readable text over Tcp—you can debug it with telnet.
But Nats doesn't just speak text over Tcp. It takes control.
Tcp's trade-offs at scale—head-of-line blocking, unpredictable buffering, slow failure detection—don't go away just because you have a nice protocol on top. Nats handles them explicitly:
Keep Tcp's reliability. Replace what doesn't work at scale.
Own connection management, own buffering, own failure detection.
Why this matters: Nats doesn't just ride on Tcp—it compensates for Tcp's weaknesses. This is why Nats can deliver millions of messages per second with predictable latency, even when clients misbehave or networks hiccup.
Core
A Different Kind of Messaging
One sender, one receiver, tight coupling everywhere—you've seen what Http can't do. Nats flips every one of those constraints.
Instead of addressing servers by location, you address messages by subject. Instead of one-to-one, you get many-to-many. Instead of request/response only, you get fire-and-forget, fan-out, and load-balanced queues—all from the same primitive.
This is the foundation everything else in Nats builds on.
Core
learn more →The foundational data communication layer for distributed systems
Core is the foundational layer that everything else in the Nats ecosystem builds on. At its heart is publish/subscribe—fire-and-forget messaging where any publisher can reach any subscriber through named subjects.
Problem: You must know the exact URL—host, port, path. Every move requires updating DNS, configs, or a service mesh.
Nats solution: Services subscribe to subjects, not endpoints. No service discovery, no load balancer config, no DNS games. Connect anywhere, reach everywhere.
Problem: Every request goes to exactly one server. Fan-out, event broadcasting, and audit trails all require separate infrastructure.
Nats solution: Many-to-many by default. Any publisher can reach any number of subscribers. Add an audit service? Just subscribe—zero changes to producers.
Why this matters: Everything that follows—subjects, pub/sub, request/reply, queue groups, Jetstream, Key Value stores—is built on top of Core. Understand this layer and the rest clicks into place.
Subject-Based Routing
learn more →Publishers and subscribers never need to know where each other are.
In Nats, you don't send messages to servers, IPs, or endpoints — you publish to subjects. A subject is just a string like orders.us.east. No admin CLI, no partition count, no pre-creation. Publish to it and it exists. Subscribers express interest in subjects, and Nats routes messages to them — regardless of where they are in the network.
The dot separator creates a natural hierarchy, and wildcards let subscribers listen to entire categories of messages without knowing every specific subject:
* — matches exactly one token> — matches one or more tokens (must be last)orders.>all ordersorders.us.*US orders (one level)orders.*.easteast region ordersorders.us.eastexact match>everythingWhy this matters: Location independence means services can move, scale, or be replaced without updating any routing configuration. A new instance just subscribes to the same subjects and starts receiving messages. No service discovery, no load balancer updates, no config changes.
Many-to-Many
learn more →Many-to-many, not point-to-point.
Publishers send to subjects. Any number of subscribers can listen. Adding a new consumer is a one-line change in the consumer—publishers don't even know. No producer modifications, no coordination, no redeployment.
Why this matters: Need audit logging across every service? Deploy a subscriber that listens to > (everything). Zero coordination, zero producer changes, instant visibility.
Publish & Subscribe
learn more →Fire-and-forget messaging.
Publish a message and Nats delivers it to all matching subscribers immediately. No disk writes. No acknowledgments. No broker consensus. Just memory-to-memory transfer.
This is at-most-once delivery—and that's a deliberate design choice, not a limitation. For real-time data—telemetry, metrics, live updates—you want the latest value, not a queue of stale ones. No redelivery storms, no message pile-ups. The system stays stable under failure.
Why this matters: Most messages don't need persistence guarantees. By making simple pub/sub the default, Nats keeps the majority of your traffic blazing fast. Save the heavyweight machinery for the messages that truly need it.
Request & Reply
learn more →RPC without the service mesh.
Need a response? Nats creates a unique inbox subject for each request. Responders publish to that inbox, and only you receive the reply. No sidecars, no Envoy config, no Istio—just pub/sub with a clever addressing trick. Send one request and collect multiple replies (scatter-gather) to find the fastest responder or aggregate results from shards. No response? The request times out cleanly—unlike Http hanging connections, failed services don't cascade into client-side thread exhaustion.
Why this matters: gRPC needs protobuf schemas, generated stubs, and often a service mesh for load balancing. Nats request/reply gives you RPC semantics with zero ceremony. Services just subscribe to their name and respond.
No Responders
learn more →Instant failure feedback, not silent timeouts.
With Http, if a service is down your request hangs until a timeout fires—30 seconds of wasted time and a blocked thread. Nats knows the subscription table. If nobody is listening on a subject, the server tells you immediately with a no responders status. No guessing, no waiting, no cascading failures from accumulated hanging connections.
Why this matters: Circuit breakers exist because Http can't tell you "nobody is home" fast enough. Nats gives you that answer in microseconds, built into the protocol. One less library to configure, one less failure mode to handle.
Queue Groups
learn more →Scaling without Kubernetes.
Add a queue group name to your subscription and Nats distributes messages across all subscribers in that group. No coordinator, no leader election, no split-brain scenarios.
Exactly one subscriber — each message goes to one subscriber in the group. Start a new worker and it gets messages immediately—stop one and others pick up instantly. No partition rebalancing delay.
Load balance — Nats distributes work evenly across workers. Scale from 1 to 1000 processes with zero configuration changes.
Fanning out — queue groups and regular subscribers coexist on the same subject. Load balance to workers while simultaneously sending to monitoring and analytics.
Why this matters: Traditional message brokers tie consumers to partitions or channels, making scaling disruptive. Nats queue groups scale from 1 to 1000 workers with zero configuration changes. Just start more processes.
Jetstream
When Things Go Down
In distributed systems, something is always down. Deploys, crashes, network blips—failure is the norm, not the exception.
Core is fast precisely because it makes no durability promises—at-most-once delivery, no persistence, no replay. If a subscriber isn't connected when a message is published, that message is gone.
So how do you keep the speed and simplicity of Nats while surviving the reality that things go down?
The Persistence Problem
Every messaging system has the same Achilles' heel.
A message broker sits between producers and consumers, holding messages in memory until they're delivered. This works beautifully—until something goes wrong.
What if a consumer crashes? Messages queue up. What if the broker itself crashes? Everything in memory vanishes. This isn't a bug in any particular system—it's a fundamental tension in distributed messaging.
Why this matters: The persistence problem isn't unique to any one tool. It's the central design tension in all messaging infrastructure. Understanding it helps you evaluate any system—and understand why Nats built Jetstream.
Jetstream
learn more →Still fast. Now durable.
Core is fire-and-forget. If no one is listening when a message is published, it vanishes. Jetstream adds persistence on top—same protocol, same subjects, but messages are stored and can be replayed.
Problem: Fire-and-forget means producers and consumers must be online at the same time. If a consumer is down, the message is gone.
Jetstream: Messages persist in streams until acknowledged. Publish now, consume later. Producers and consumers are fully decoupled in time.
Why this matters: Most messaging systems force a choice: fast-and-ephemeral or durable-and-heavy. Nats gives you both. Core for real-time fire-and-forget, Jetstream for when every message must be accounted for. One binary, one protocol, two modes.
Persistence
Publish now, consume later.
Jetstream offers a durability spectrum. More durability means more work per message—pick the right trade-off per stream.
Memory — fastest. Messages live in RAM and are lost on restart. Ideal for caches and ephemeral state.
Disk — durable. Messages survive server restarts. The default for most workloads.
Replicated — safest. Messages are written to multiple servers. Survives machine failures with no data loss.
Persistence enables temporal decoupling. Subscribers can go offline, come back, and catch up on missed messages automatically.
Why this matters: Most brokers force you to choose persistence upfront. Nats lets you mix: telemetry over Core (fast, ephemeral), orders through Jetstream (durable, guaranteed). One system, right tool for each job.
Streams
learn more →Append-only logs that capture messages by subject.
A stream is an append-only log bound to one or more subjects. Every matching message is stored in sequence. Retention policy controls when messages leave the log:
Limits-based — cap by max messages, max bytes, or max age. Oldest messages are discarded when limits are hit.
Interest-based — keep messages until all consumers have seen them. Nothing is discarded while there's still interest.
Work-queue — delete on ack. Each message is processed exactly once, then removed from the stream.
Why this matters: Streams decouple storage from delivery. Publishers fire messages into subjects as usual—streams silently capture them. No code changes on the publish side, no new API to learn.
Consumers
learn more →Durable cursors that track delivery progress.
Consumers act as durable cursors, tracking delivery progress independently. Messages redeliver until acknowledged, guaranteeing at-least-once delivery. For critical paths, idempotent publishing with message deduplication provides exactly-once semantics. When a consumer disconnects, Jetstream remembers its position. On reconnect, missed messages replay automatically.
Why this matters: Each consumer is independent—add an analytics consumer alongside your processing consumer without affecting either. Multiple readers, one stream, zero interference.
Data Stores
Beyond Messages
Streams solved durability. But your app needs more than a log of messages.
Configuration that services read on startup. Session data that changes mid-flight. ML models too large for a single message. Every distributed system eventually needs state and files alongside its event stream—and traditionally that means bolting on a separate Key Value store, object store, and another SDK.
What if the infrastructure you already have could handle all three?
Data Stores
Most platforms cover one or two of these well, but not all three. Message brokers add key-value APIs as an afterthought. Key-value stores bolt on pub/sub. Nobody covers all three over a single protocol.
Nats does. A key-value pair is just a subject with the latest message retained. A large file is a sequence of chunked messages in a stream. Same protocol, same connection, same replication—no new infrastructure.
Problem: Running a message broker, a Key Value store, and an object store means three separate systems to deploy, monitor, and keep consistent.
Nats: A Key Value pair is a subject with the latest message retained. A file is chunked messages in a stream. One protocol, one connection, one cluster—no extra infrastructure.
Why this matters: Instead of running a separate Key Value store and object store alongside your message broker, Nats gives you messages, state, and file storage from the same binary you're already running. One system to deploy, monitor, and reason about.
Key Value Store
learn more →Key-value storage over your existing Nats connection.
Nats Key Value is a key-value store built on top of Jetstream. Get, put, delete, and watch keys—all over your existing Nats connection.
Every Key Value bucket is backed by a Jetstream stream. Keys map to subjects, values to message payloads, and revisions to sequence numbers. A put("user.123.name", "Alice") becomes a publish to $KV.users.user.123.name. Watchers are just Jetstream consumers with subject filters.
Watch — subscribe to key patterns like user.123.> and get notified on every change in real-time. No polling. Build reactive UIs, configuration hot-reload, or distributed coordination.
TTL — keys expire automatically after a configured duration. Clean up stale state without manual intervention.
History — retain previous values per key with atomic compare-and-swap for safe concurrent updates.
user.123.name"Alice"r1user.123.status"online"r1config.theme"dark"r3user.123.location"NYC"r1user.123.>Why this matters: A dedicated Key Value store means another cluster to manage. Nats Key Value gives you the same get/put/watch semantics over the connection you're already using for messaging. One system, fewer moving parts, and replication comes free from Jetstream.
Object Store
learn more →Large blob storage over Nats.
Store files, images, ML models—anything up to gigabytes. Nats automatically chunks large objects into Jetstream messages, handles replication across the cluster, and reassembles them on read. Like S3, but without the separate service.
Each object is split into fixed-size chunks (default 128 KB) and published to a dedicated Jetstream stream. Metadata—name, size, content type, checksum—is stored in a companion stream. On read, the client fetches chunks in order and verifies the checksum.
Watch for changes — subscribe to object updates just like Key Value watchers. Get notified when a model is updated, a config file changes, or a new artifact is published.
Replicated — inherits Jetstream replication. Configure R=3 and your objects survive server failures. No need for a separate distributed file system.
Why this matters: S3 is durable but adds latency and another SDK. Nats Object Store gives you blob storage over the same connection you're already using for messaging and Key Value. One system for messages, state, and files.
Scaling
Scaling Beyond One Server
Streams, key-value pairs, object storage—all from a single binary. But real systems don't run on a single server.
Production means multiple regions, cloud providers, and edge devices. It means tolerating failures without downtime. It means messages published in Tokyo reaching subscribers in Frankfurt without your code knowing the difference.
Nats was built for this from the start—servers form a mesh automatically, routing messages only where they're needed across clusters, superclusters, and leaf nodes.
Clustering
Connect anywhere, reach everywhere.
Nats servers form clusters so your code doesn't change whether subscribers are local, in another region, or on the edge—the network figures it out. Interest-based routing means messages only flow where subscribers exist—Nats doesn't copy data to regions with no listeners, so bandwidth is automatically optimized.
Leaf nodes — extend Nats to the edge via a 20MB binary that runs on a Raspberry Pi—factories, stores, vehicles, anywhere with intermittent connectivity. Messages queue locally during outages.
Clusters — a group of Nats servers that share clients and messages. They form a full mesh automatically—publish to any node, subscribers on any other node receive it.
Superclusters — connect multiple clusters via gateway connections for global reach. Each cluster operates independently, but messages route seamlessly across all of them.
Problem: Cross-region messaging usually requires separate replication tooling, connection routing, and careful configuration for each new region.
Nats: Servers discover each other and form a full mesh automatically. Publish to any node, subscribers on any other node receive it. Interest-based routing means messages only flow where listeners exist.
Why this matters: Nats clustering is declarative—list your servers and they form a mesh. Location independence isn't a feature, it's the architecture.
Leaf Node
learn more →Extend Nats to the edge—factories, stores, vehicles, anywhere.
Single upstream connection — a standalone Nats server (20MB binary) connects to a cluster. Runs on a Raspberry Pi, an industrial gateway, or a vehicle's onboard computer—anywhere a full cluster member would be overkill.
Subject filtering — control exactly which subjects bridge between the leaf and the upstream cluster. A factory floor node might only sync sensors.> upstream while keeping local traffic private. Only the data you choose leaves the edge.
Resilient at the edge — when the upstream connection drops, the leaf node keeps running. Local publishers and subscribers continue communicating. With Jetstream enabled, messages destined for the cluster queue until connectivity is restored—no data loss, no application changes.
Why this matters: Edge deployments usually mean a completely separate messaging stack with custom sync logic. Leaf nodes give you the same Nats subjects, the same client libraries, and the same publish/subscribe semantics—just closer to the data source.
Cluster
learn more →Full-mesh routes within a region—every server can reach every subscriber.
Full-mesh route connections — every server maintains a direct Tcp link to every other server in the cluster. Publish to any server, and it reaches all subscribers regardless of which server they're connected to.
Auto-discovery — point a new server at any existing member and it learns the full topology. No static config files listing every node. If one goes down, clients automatically reconnect to a surviving member.
Transparent failover — clients connect to multiple servers and failover automatically. No connection pooling libraries, no retry logic to write, no circuit breakers to configure—the client library handles it.
Why this matters: Nats core routing is symmetric—every server can route messages to every other. For Jetstream, Nats uses the Raft consensus algorithm to elect a leader per stream, ensuring consistency without a single external coordinator.
Raft Consensus
learn more →How Jetstream keeps streams consistent across a cluster
One Raft group per stream — Jetstream runs a separate Raft consensus group for each stream and each consumer. No single leader bottleneck—each group elects its own leader independently.
Meta group for placement — a cluster-wide meta group (all Jetstream-enabled servers) decides where to place new streams and consumers. Each stream group then handles its own data replication; each consumer group tracks delivery state.
Quorum writes — a message is only acknowledged once a majority of replicas have written it. With R3, that means 2 of 3 servers must confirm before the publisher gets an ACK.
Why this matters: Raft gives Jetstream strong consistency without external dependencies like ZooKeeper or etcd. Every stream gets its own consensus group, so a busy stream can't block an unrelated one.
Supercluster
learn more →Gateways between regions—global reach without full-mesh overhead.
Gateway connections — each cluster elects a gateway pair that maintains a single logical link to every other cluster. No full-mesh between regions—just targeted hops.
Interest-based routing — messages only traverse gateways when subscribers exist in the remote region. Publish orders.us.east in New York and nobody in Frankfurt is listening? The message never leaves the US cluster.
Accounts and security boundaries are shared across the supercluster, so a service in any region can reach any other service on the same account—same subjects, same permissions, no extra configuration per region.
Why this matters: Traditional multi-region messaging requires dedicated replication pipelines per topic, manual failover runbooks, and careful bandwidth budgeting. Superclusters make it declarative—list your clusters and gateways handle the rest.
Security
Locking It Down
Clusters spanning regions, leaf nodes at the edge, messages flowing everywhere—but who's allowed to connect? And what stops one tenant's data from leaking into another?
Most messaging systems bolt security on after the fact—an external auth service here, a firewall rule there. Nats takes the opposite approach: authentication, authorization, and multi-tenancy are built into the protocol itself.
No external databases to secure. No config file restarts to add users. Just cryptographic identity baked into every connection.
Security
learn more →Secure by default, decentralized by design.
TLS encryption is a flag away. Accounts are isolated by default—multi-tenant by design, messages in one account are invisible to others, shared only through explicit exports/imports. Auth scales from simple credentials to fully decentralized identity:
Tokens — a single shared secret string. The simplest option for development and internal services.
Username/Password — familiar credentials with optional bcrypt hashing. Easy to set up, easy to reason about.
NKeys — Ed25519 key pairs where the server stores only public keys—private keys never leave the client. Nothing valuable to steal.
Decentralized JWTs — account operators issue credentials without touching server config. Add users, revoke access, change permissions with no server restart and no config file edits.
Auth Callout — delegate authentication to your own external service. Plug in LDAP, OAuth2, or any custom identity provider without forking the server.
Why this matters: Most message brokers need external auth systems for production. Nats bakes multi-tenancy into the protocol. Onboard new customers without server changes. Revoke access instantly. Keep tenants isolated without running separate clusters.
Auth Callout
learn more →Your auth rules. Nats enforcement.
NKeys and JWTs handle most scenarios, but what if credentials already live in an LDAP directory, an OAuth provider, or a custom database?
Auth Callout — lets Nats delegate authentication to your own service—a regular Nats subscriber that receives connection requests, validates credentials against any backend, and returns a signed JWT with scoped permissions.
The callout service subscribes to $SYS.REQ.USER.AUTH. When a client connects, Nats publishes the connection details to that subject. Your service validates, builds a user JWT, signs it, and replies. Nats enforces the permissions—your service owns the decision.
Why this matters: Most brokers force a choice: use their auth system or build a custom plugin in their language. Auth Callout lets you write validation logic in any language, against any backend, deployed as a regular Nats client. Swap auth providers without touching server config.
Why Not Just Use ___?
But What About ___?
Messaging, persistence, data stores, clustering—all from one system. So why does anyone reach for something else?
Kafka has been the default for event streaming. RabbitMQ owns the traditional message broker space. Redis is everyone's first cache. Each is battle-tested, well-documented, and already in your stack. The question isn't whether they work—it's what you pay in complexity when your needs span more than one of them.
Let's see how Nats stacks up—and where the trade-offs actually are.
At a Glance
Before diving into the details, here's what each system gives you out of the box.
Protocol & Fundamentals
How each system talks on the wire, names its destinations, and layers on persistence.
The protocol shapes everything else—how easy it is to debug, how fast you can onboard, and how much ceremony stands between you and your first message. Compare the foundations.
Plain text over Tcp. You can debug it with telnet. Write a client in any language in an afternoon.
Custom binary protocol. Requires a client library — no telnet debugging.
AMQP 0-9-1 — complex binary framing with channels, exchanges, and bindings baked into the wire format.
RESP protocol. Simple, but no built-in routing, wildcards behave differently, and no queue groups.
ZMTP binary protocol. Powerful socket types, but no broker — you wire topology by hand. No subject routing, no discovery, no clustering.
Why this matters: A simple protocol means fewer dependencies, faster debugging, and clients in every language. Nats gives you that simplicity at the wire level, and Jetstream layers persistence on top without changing the protocol.
Messaging Patterns
Every messaging system claims pub/sub. Few give you the rest without extra infrastructure.
Pub/sub, request/reply, queue groups, and back-pressure are the building blocks of real-time messaging. See how Nats handles each one natively, while alternatives require workarounds, extra components, or manual plumbing.
Built into the core protocol — fire-and-forget by default. No offsets, no replay, no pile-up. Messages that nobody is listening for simply disappear.
Every message hits disk and gets an offset. Consumers replay from last committed offset — sometimes thousands of stale messages after a crash.
Requires declaring exchanges, queues, and bindings before a single message flows. Slow consumers cause memory pressure on the broker.
Simple fire-and-forget pub/sub with glob wildcards via PSUBSCRIBE, but no dot-delimited subject hierarchy and no fan-out control.
PUB/SUB socket types exist, but you manage connections and topic filtering yourself. No broker means no fan-out guarantees — if a subscriber isn't connected, the message is gone.
Why this matters: Every alternative solves one or two patterns—pub/sub here, queuing there, request/reply somewhere else. Nats Core handles all of them natively, in a single binary, with zero external dependencies.
Persistence & Streaming
Jetstream isn't the first persistent streaming system. But it's the only one that isn't a separate system.
Kafka, Pulsar, and RabbitMQ Streams were built for durable streaming too. The difference is in what else they bring along: separate protocols, separate clusters, separate operational burdens. Jetstream is a capability you turn on inside the Nats server you already run.
Choose per-subject: ephemeral telemetry over Core, durable orders through Jetstream. One system, right guarantee for each message.
Every message hits disk, even ephemeral telemetry you'll never read again. You can set very short retention per-topic, but messages always touch disk first.
Tiered storage helps offload old data. Non-persistent topics exist for ephemeral data, but persistent topics always flow through BookKeeper first.
Quorum queues add durability, but at the cost of throughput. Classic queues are faster but can lose messages on node failure.
Why this matters: Kafka and Pulsar are powerful distributed logs. But they are separate systems with separate protocols, separate operational burdens, and separate failure modes. Jetstream gives you durable streams, key-value storage, and object storage—all inside the same binary that handles your real-time messaging.
Operations & Security
Running at scale means clustering, security, and multi-tenancy. Nats builds all three into the server.
Clustering shouldn't require a coordinator service. Auth shouldn't require an external system. Multi-tenancy shouldn't be a naming convention. See how Nats handles operations and security compared to the alternatives.
Full-mesh clustering with zero-config gossip protocol. Add a node, point it at any existing node, and the cluster self-organizes. Superclusters span regions with gateway connections.
Requires KRaft for metadata consensus. Broker addition needs partition reassignment. Cross-region replication is a separate product (MirrorMaker).
Clustering works within a LAN but breaks across regions. Federation and shovels exist but add operational complexity.
Redis Cluster shards data by hash slots. Adding nodes means resharding. Sharded Pub/Sub (Redis 7.0+) participates in the cluster protocol, but classic Pub/Sub does not.
No clustering — it's a library, not a server. You build your own topology with broker patterns (ROUTER/DEALER), but there's no automatic failover or discovery.
Why this matters: Clustering that self-organizes and security that's built into the protocol mean fewer moving parts in production. No separate metadata service, no external auth backends, no separate federation plugins.
The Full Picture
The Full Picture
We've seen how Nats compares to the alternatives. Now let's step back and see the full picture.
Four layers in a single binary—core messaging, persistent streams, data stores, and clustering. Most apps only need one or two. The question is which layers fit your problem, and when to reach for each.
One System, Not Eight
The others make you stitch together separate services. Nats is one unified system—not a bundle of features, but layers built on a single foundation.
Most microservice architectures require a service mesh for discovery, a load balancer for routing, a message broker for async communication, a cache for shared state, object storage for large payloads, multi-region replication, and a separate auth layer. That's eight systems with eight protocols, eight auth configurations, and eight failure modes. Nats replaces them not by bundling eight tools together, but because everything—pub/sub, streaming, Key Value, object storage—is built on the same subjects, connections, and security model.
Sidecars, mTLS, traffic routing, retries
Registry, health checks, DNS
Routing rules, sticky sessions, health probes
Topics, partitions, consumer groups, schemas
Session state, distributed locks, pub/sub
Blobs, file artifacts, large payloads
Cross-region replication, edge compute, data locality
Identity, token management, tenant isolation
Why this matters: Every additional system is another protocol, another cluster, another 3am failure mode. Nats isn't a Swiss Army knife of bolted-on features—it's a layered architecture where Key Value is built on Jetstream, Jetstream is built on Core, and everything shares one subject namespace, one connection, and one auth model. One binary to deploy, one system to monitor, one set of skills to learn.
Putting It Together
Start simple. Layer on capabilities as your needs grow.
Nats gives you a toolkit of capabilities that share one subject namespace, one connection, and one security model. Most applications start with Core and add capabilities only when needed. Security wraps everything—enable it at any point, and it applies across all layers.
Start simple. Add complexity only when needed.
That's the Nats philosophy—and why it scales from IoT sensors to global financial systems.
