JetStream backups are actually just point-in-time copies of your stream’s message log, not a full database dump, and they can be restored in place over the existing stream.

Let’s see this in action. Imagine we have a stream named orders with some messages.

nats stream add orders --subjects orders > /dev/null
nats pub orders "new order 1"
nats pub orders "new order 2"
nats pub orders "new order 3"

Now, let’s take a snapshot. The js snapshot command needs a stream name and an output file. We’ll use a temporary file for this.

nats js snapshot orders ./orders.snapshot

This creates a file, ./orders.snapshot, containing the current state of the orders stream. It’s not a raw log file; it’s a structured representation of the messages and their metadata.

To restore, we use js restore. Crucially, you specify the target stream name and the snapshot file. The restore operation will overwrite the existing stream’s data with the snapshot’s data.

Let’s simulate a problem by deleting the stream and recreating it with different settings, then restoring.

nats stream rm orders
nats stream add orders --subjects orders --max-msgs 10 --max-bytes 10000 > /dev/null
nats pub orders "order 4"
nats pub orders "order 5"
nats stream info orders # See current state

Now, let’s restore from our snapshot:

nats js restore orders ./orders.snapshot
nats stream info orders # Observe the restored state

You’ll notice that the stream orders now contains messages "new order 1", "new order 2", and "new order 3" again, and its configuration (like max-msgs and max-bytes) reverts to what it was when the snapshot was taken. This is because the restore operation replaces the stream’s internal state.

The core problem JetStream backups solve is disaster recovery for your message streams. If a stream’s data becomes corrupted, or if you accidentally delete it, a snapshot allows you to bring it back to a known good state. It’s also useful for cloning a stream’s state to a new stream for testing or analysis.

Internally, a JetStream snapshot is a JSON file. It contains metadata about the stream (like its configuration, retention policies, etc.) and a list of messages. Each message entry includes its sequence number, subject, payload (base64 encoded), and headers. When you restore, JetStream essentially rebuilds the stream’s log from this manifest.

The js snapshot command has a few useful flags. --max-seq allows you to snapshot up to a specific message sequence number, and --before lets you snapshot messages published before a certain time. This is powerful for creating very precise recovery points.

The js restore command also has a --force flag, which is critical. If the target stream already exists and has data, restore will fail by default to prevent accidental data loss. Using --force will delete the existing stream and recreate it based on the snapshot’s configuration and data. Be very careful with this.

One thing most people don’t realize is that the snapshot file itself is not directly usable as a stream. It’s a serialized representation. You must use js restore to apply it. Also, when restoring, JetStream recreates the stream with the configuration that was active at the time the snapshot was taken. Any changes made to the stream’s configuration after the snapshot was taken will be lost during the restore. This is why it’s crucial to understand that you’re restoring the entire stream state, not just the messages.

The next common problem you’ll encounter is managing snapshot rotation and retention policies for your backups.

Want structured learning?

Take the full Nats course →