Skip to main content

Altering stream state

Retention limits remove messages for you, on a schedule the server runs. Sometimes you need to remove something by hand: one bad message a buggy producer wrote, or every message in a stream you're clearing out for a fresh test run.

JetStream gives you two manual tools for that. You can delete a single message, or purge many at once. Both change what the stream holds without touching its config or its consumers, and both leave the sequence numbers alone. How the numbers behave is worth understanding before you use either tool.

Delete one message

Every stored message has a sequence number, the one the PubAck gave back when you published it. To remove a specific message, give the server that number:

nats stream rmm ORDERS 2

rmm is "remove message." It takes the stream and the sequence to drop, and it asks for confirmation first (Really remove message 2 from Stream ORDERS). Add --force to skip the prompt in a script.

nats stream rmm securely removes the message: the server overwrites the stored bytes so the old contents can't be read back. That's the right default for a message that held data it shouldn't have, such as a card number a producer logged by mistake.

From a client library you choose that cost yourself. DeleteMsg(seq) marks the message erased but leaves its bytes in place until they're later overwritten, which is cheap. SecureDeleteMsg(seq) overwrites them right away the way the CLI does, and is slower for it. The only difference the server sees is a single no_erase flag on the delete request; the full request is in Reference.

Purge the stream

To clear a whole stream at once, purge it:

nats stream purge ORDERS

That removes every message in ORDERS and reports how many it dropped (Purged 3 messages from ORDERS). Like rmm, it asks for confirmation first; add --force to skip the prompt in a script. The stream itself stays: same config, same consumers, same name. Only the messages go. (Don't confuse it with nats stream rm ORDERS, which deletes the whole stream, config and consumers included.)

A bare purge removes everything. Three optional flags narrow it:

# Remove only the shipped events, leave everything else
nats stream purge ORDERS --subject orders.shipped

# Remove everything up to but not including sequence 100 (keep 100 onward)
nats stream purge ORDERS --seq 100

# Keep only the most recent 50 messages
nats stream purge ORDERS --keep 50

--subject limits the purge to one subject. --seq counts from the bottom (drop everything older than a sequence); --keep counts from the top (retain a number of the newest). You can't use --seq and --keep together, since one works from each end, but either one can pair with --subject to count within a single subject.

Sequence numbers never backfill

Delete a message or purge a range and you leave a hole. Sequence 2 is gone; 1 and 3 stay exactly where they were. The server does not renumber 3 down to close the gap, and it never hands 2 out again to a future message.

This is the rule the publishing page promised: sequence numbers only ever climb. After a full purge of ORDERS, the next message you publish doesn't start over at 1. It gets the number after the last one the stream ever held, because purge sets the stream's first sequence to one past its last.

Consumers handle the gaps without trouble. A consumer reading ORDERS skips the sequences that are gone; it never blocks waiting for a deleted message, and a missing 2 is not redelivered.

This means a stored sequence is a stable address. If you saved "order ord_8w2k is at sequence 2" alongside your business record, that pointer either still points at the same message or points at nothing. It never points at a different message, because 2 never gets reused.

Manual removal versus automatic

Everything on this page is manual: you ran a command to remove something deliberately. The server also removes messages on its own, and that lives on two other pages:

  • Shaping the streamMaxAge, MaxBytes, MaxMsgs, and discard policy trim the stream to stay within its limits.
  • Per-message TTL — a single message expires on its own clock.

Use delete and purge for one-off removals. Use limits and TTL for the steady state.

Pitfalls

Some ways manual removal goes wrong.

purge empties the stream; rm destroys it. The two commands look similar but do different things. nats stream purge ORDERS removes the messages and leaves the stream ready for more. nats stream rm ORDERS deletes the stream itself, including its config and every consumer. Use purge when you only mean to clear messages; don't type rm for that, and read the confirmation prompt before answering it.

A deleted sequence is gone, not renumbered. Code that assumes messages run 1..N with no holes breaks the first time anyone removes one. Do treat a sequence as a stable address that may or may not still hold a message; don't work out "the next message" as count + 1, and don't read a message count as the highest sequence.

A purge can't be undone. The messages are gone the moment the command returns. On a stream that matters, set DenyPurge in its config so a stray call can't wipe it, and keep a mirror if the data is worth recovering.

# Guard a stream so purge requests are refused
nats stream edit ORDERS --deny-purge

# A purge now fails instead of silently emptying the stream
nats stream purge ORDERS

Where you are

You can now remove messages from ORDERS by hand: one at a time with nats stream rmm, or many at once with nats stream purge and its --subject, --seq, and --keep flags. You saw that removal leaves gaps the server never fills back in, and that the sequence counter only ever climbs.

If you ran a full nats stream purge ORDERS above, the stream is empty now; republish the three orders from the publishing page to carry a populated ORDERS into the next page. Everything else about the stream, its config and its consumers, is exactly as the previous page left it.

What's next

Manual and automatic removal are both covered, so the stream's contents are fully in your hands on a single server. The next page asks what happens to all of it when the server itself goes away.

See also