This episode introduces JetStream as NATS's persistence layer: the concept of a stream as a persistent log, retention and durability, how to enable JetStream, and the message flow from publish into a stream and from a stream to a consumer.

Up to episode 6, you've been working with Core NATS: fast and lightweight, but messages aren't stored. Episode 7 opens a new chapter: JetStream, the persistence layer that makes NATS a durable messaging system. This is why NATS can compete with stateful streaming brokers.
We'll cover the concept of a stream as a persistent log, retention and durability, how to enable JetStream, and the full flow from publishing into a stream and from a stream to a consumer. This is the foundation for episodes 8 and 9.
Core NATS is designed at-most-once: messages are delivered as fast as possible and dropped if there's no subscriber. As a result, messages can be lost when a subscriber is offline, a service is restarting, or a publisher crashes after sending.
publisher --> subject --> (no subscriber) --> message droppedThe message dropped scheme above is unacceptable for payments, job queues, or audit logs. JetStream answers: store messages on the server until a consumer is ready to receive them.
JetStream provides an at-least-once guarantee: messages are stored in the stream, delivered to the consumer, and only removed after the consumer acks. If the consumer fails to ack within the time limit, the message is redelivered. No message is lost — at worst, a message is delivered more than once.
A stream is a message log that captures certain subjects. A stream has an identity, a list of subjects, and storage rules:
stream ORDERS:
subjects: orders.>
retention: limits
storage: file
max_age: 30 hari
max_bytes: 1 GBThe stream ORDERS block above captures all orders.> messages, storing them on disk for at most 30 days or 1 GB. Full details of each field will be covered in episode 8.
A consumer is the entity that reads messages from a stream. It has its own state: which messages have been delivered, which await ack, which must be redelivered. A consumer can be push (the server sends to the client) or pull (the client requests).
| Characteristic | Core NATS | JetStream |
|---|---|---|
| Storage | None | File/memory-based stream |
| Guarantee | At-most-once | At-least-once with ack |
| Replay | Not possible | Possible from a specific sequence |
| State | Stateless | Consumer has state |
The difference between at-most-once and at-least-once is the main dividing line between NATS's two modes.
JetStream can be enabled when starting the server with the -js flag:
nats-server -p 4222 -jsnats-server -p 4222 -js enables JetStream with default configuration. For production, use a configuration block:
jetstream {
store_dir: /data/jetstream
max_mem_store: 1G
max_file_store: 10G
max_mem: 512M
}The jetstream block sets the storage location and resource limits. Without this block, JetStream uses a store in a temporary directory — not suitable for data that must survive.
With JetStream active, create your first stream via the CLI:
nats stream add ORDERS --subjects "orders.>" --storage file --retention limitsnats stream add ORDERS --subjects "orders.>" creates an ORDERS stream that captures all orders.> subjects. The CLI will ask a few interactive options; answer with the default values for now.
The JetStream flow starts with publishing. Messages published to a subject on the stream's list are copied into the stream:
nats pub orders.created "pesanan-1"
nats stream view ORDERSnats stream view ORDERS shows every message stored in the stream. Notice: the pesanan-1 message is stored even though there's no subscriber — this is the power of JetStream.
Info
Publishing to JetStream differs from Core NATS: use nats pub with the ack option, or publish from a client library with the publish-async and publish-ack options. The server returns an acknowledgment after the message is safely in the stream — a guarantee that the data was received.
publisher
└── publish to subject:orders.created
└── stream ORDERS stores the message
└── consumer NEW reads the message
└── application processes and acksThe flow above is the heart of the JetStream mental model: publish enters the stream, the stream stores, the consumer delivers, the application acks. Episodes 8 and 9 will break down each layer.
Make sure messages are truly stored:
nats stream info ORDERS
nats stream reportnats stream info ORDERS shows the message count, size, and the last message time. nats stream report summarizes the health of all streams. These two commands will be your troubleshooting companions in episode 19.
Episode 7 opened the door to JetStream: understanding the at-most-once limitation of Core NATS, the concept of a stream as a persistent log and a consumer as a stateful reader, at-least-once via ack, enabling JetStream with -js or a configuration block, and the publish-to-stream-to-consumer flow.
Key takeaways:
-js flag or a jetstream block in configuration.nats stream add creates a stream; nats stream view shows its contents.In episode 8 next, we'll discuss JetStream streams — the subjects, retention configuration with the Limits, Interest, and WorkQueue modes, storage as file or memory, the max_age and max_bytes limits, replicas replication, as well as stream sourcing, mirroring, and the dedupe window for exactly-once publishing. This is where streams are configured with precision.