Durability & Persistence
The restart test
Section titled “The restart test”Everything so far assumed the broker keeps running. But brokers restart — deploys, crashes, kernel updates. The question that separates a toy setup from a production one is: when RabbitMQ restarts, what’s still there?
By default, disappointingly little. A queue and its messages live in memory, and a restart wipes them unless you asked for durability — and here’s the catch that trips up almost everyone: it takes two independent settings, and one without the other still loses your data.
Two settings, both required
Section titled “Two settings, both required”Surviving a restart needs both of these to be true:
- The queue is durable. A durable queue’s definition is written to disk, so the queue still exists after a restart. Set
durable: truewhen you declare it. - The message is persistent. A persistent message’s body is written to disk. Set
delivery_mode = 2(persistent) when you publish it.
Miss either one and you lose data on restart:
flowchart TB a["durable queue + persistent message"] -->|restart| a2["survives"] b["durable queue + transient message"] -->|restart| b2["queue stays, MESSAGES LOST"] c["non-durable queue + persistent message"] -->|restart| c2["QUEUE GONE (messages with it)"] d["non-durable + transient"] -->|restart| d2["all lost"]
The classic production incident: a team declares a durable queue, feels safe, and forgets to publish messages as persistent. The queue faithfully survives every restart — empty, because the messages inside it were transient. Durable queue, transient messages: the queue lives, the data dies.
// 1) durable queue — survives restartawait channel.assertQueue('orders', { durable: true });
// 2) persistent message — body written to diskchannel.sendToQueue('orders', Buffer.from(body), { persistent: true });# 1) durable queuechannel.queue_declare(queue="orders", durable=True)
# 2) persistent message (delivery_mode=2)channel.basic_publish( exchange="", routing_key="orders", body=body, properties=pika.BasicProperties(delivery_mode=2),)// 1) durable queuech.QueueDeclare("orders", true, false, false, false, nil)
// 2) persistent messagech.PublishWithContext(ctx, "", "orders", false, false, amqp.Publishing{ DeliveryMode: amqp.Persistent, // = 2 Body: body,})The trade-off: durability costs latency
Section titled “The trade-off: durability costs latency”Writing to disk is slower than staying in memory, so persistence has a cost. But note an important subtlety: marking a message persistent does not guarantee it’s on disk the instant you publish — RabbitMQ may batch writes. To be certain a message is safely stored before you consider it sent, pair persistence with publisher confirms (previous lesson): the confirm arrives only after the message is safely persisted.
So the honest picture: durable queue + persistent message + publisher confirms is the fully-safe combination, and it trades some throughput and latency for not losing data across a restart. For high-volume, loss-tolerant streams (metrics, logs) you may deliberately skip persistence for speed — a legitimate choice, made consciously.
A note on quorum queues
Section titled “A note on quorum queues”Classic durable queues survive a restart of one node — but a classic queue lives entirely on a single node, so if that node’s disk dies, the queue dies with it. Quorum queues raise the bar: they replicate the queue across several nodes using the Raft consensus algorithm, so the data survives the loss of a whole node, not just a restart. They’re the modern default for anything that must not be lost — and the Operations & Scaling module covers them in depth.