Skip to main content

4. Persist messages with JetStream

So far your messages have been fleeting: a subscriber that isn't listening at the moment you publish never sees them. In this tutorial you turn on JetStream, create a stream that stores messages durably, publish a few, and then replay them from the very beginning. By the end you'll have proof that the messages survived, sitting in storage, ready to read again.

What you'll need

  • nats-server and the nats CLI installed (from Hello NATS).
  • A terminal. You'll start the server in one and run commands in another.

Step 1: Start the server with JetStream

Stop any server you started earlier, then start a new one with the -js flag. That flag turns on JetStream, the subsystem that stores messages.

nats-server -js

You should see a startup log that mentions JetStream, including a line like:

[INF] Starting JetStream
[INF] Server is ready

Leave this running. Open a second terminal for the remaining steps.

Step 2: Create a stream

In the second terminal, create a stream named EVENTS that captures every subject beginning with events.. The --defaults flag fills in sensible starting values so you aren't prompted for anything.

#!/bin/bash

# Create a stream named EVENTS that captures every subject under events.>
# --defaults fills in sensible starting values so the CLI does not prompt.
nats stream add EVENTS --subjects "events.>" --defaults

You should see output ending with:

Stream EVENTS was created

Your stream now exists and is waiting for messages.

Step 3: Publish a few messages

Publish three messages to subjects under events.. Because each subject matches the stream, the server stores every one.

#!/bin/bash

# Publish three messages. Each subject starts with events. so the EVENTS
# stream captures and stores every one. For each publish the CLI prints a
# "Stored in Stream" line with the assigned sequence, confirming it was stored.
nats pub events.page_loaded '{"page":"/home"}' --jetstream
nats pub events.input_changed '{"field":"email"}' --jetstream
nats pub events.page_loaded '{"page":"/pricing"}' --jetstream

For each publish you should see two lines: one confirming the message was sent, and one confirming the stream stored it with an assigned sequence number:

13:42:01 Published 16 bytes to "events.page_loaded"
13:42:01 Stored in Stream: EVENTS Sequence: 1
13:42:01 Published 17 bytes to "events.input_changed"
13:42:01 Stored in Stream: EVENTS Sequence: 2
13:42:01 Published 19 bytes to "events.page_loaded"
13:42:01 Stored in Stream: EVENTS Sequence: 3

Confirm the stream now holds three messages:

nats stream info EVENTS

You should see a State block reporting three messages:

State:

Messages: 3
Bytes: 159 B
First Sequence: 1 @ 2026-06-09 13:42:01
Last Sequence: 3 @ 2026-06-09 13:42:01
Active Consumers: 0
Number of Subjects: 2

Step 4: Replay the stored messages

Now read the messages back. This replays every message the stream holds, oldest first, starting from the very first one. Nothing is removed: replaying a stream is a read.

#!/bin/bash

# Replay every message stored in the stream, oldest first.
# --all start at the first stored message (sequence 1)
# --terminate-at-end stop once all stored messages have been read
nats sub "events.>" --all --terminate-at-end

You should see all three messages, in the order they were published:

[#1] Received JetStream message: stream: EVENTS seq: 1 / pending: 2 / subject: events.page_loaded / time: 2026-06-09 13:42:01
{"page":"/home"}

[#2] Received JetStream message: stream: EVENTS seq: 2 / pending: 1 / subject: events.input_changed / time: 2026-06-09 13:42:01
{"field":"email"}

[#3] Received JetStream message: stream: EVENTS seq: 3 / pending: 0 / subject: events.page_loaded / time: 2026-06-09 13:42:01
{"page":"/pricing"}

The command exits on its own once it's read everything stored. Run it again and you'll see the same three messages: the stream still has them.

What you built

You enabled JetStream, created the EVENTS stream, published three messages into it, and replayed all three back from storage. And they were still there to read again afterward.

Next