Yes, RabbitMQ can lose messages — almost always because one link in the durability chain is missing. RabbitMQ message durability requires a durable queue, a message published with delivery mode 2, a publisher that waits for a confirm, and a consumer that acknowledges after processing. Any one of those missing, and loss is possible.
The single most common mistake in RabbitMQ message durability is assuming a durable queue is enough. It is not, and the documentation is explicit about why: durable queues are recovered on node boot including messages published as persistent, while transient messages are discarded during recovery even if they were sitting in a durable queue.
Can RabbitMQ lose messages? The honest answer
Yes, in five distinct ways, and they are worth naming separately because the fixes differ.
- The publisher never knew. The client wrote frames to its socket and moved on. RabbitMQ's publisher documentation is direct: a client that has written a frame to its socket cannot assume the message reached the server. Without confirms, a message lost in flight looks like a successful publish.
- The broker had it in memory only. RabbitMQ does not write a message to disk the instant it arrives. If the node dies in that window, a message you believed was persistent is gone.
- The queue was not durable, or the message was not persistent. Either alone is insufficient.
- The consumer took it and died. With automatic acknowledgement, RabbitMQ considers a message delivered the moment it hits the socket. If the consumer crashes mid-processing, nothing redelivers it.
- Dead-lettering dropped it. The default dead-letter strategy republishes without confirms, so a message removed from the source queue can vanish if the target is unreachable.
Every RabbitMQ message durability incident we have worked is one of those five.
What is the RabbitMQ message durability chain?
RabbitMQ message durability is four links, each with a distinct owner.
| Link | Set by | What it protects against | Failure if missing |
|---|---|---|---|
| Durable exchange and queue | Declaration (durable: true) | Topology loss on restart | Queue and bindings gone after a node boot |
| Persistent delivery mode (2) | Publisher, per message | Message loss on restart | Message discarded during recovery |
| Publisher confirms | Publisher, confirm.select | Loss in flight or before disk write | Publisher believes a lost message succeeded |
| Consumer acknowledgement | Consumer, basic.ack | Loss during processing | Message discarded when the consumer dies |
These are not alternatives. They are serial, and a message is only as safe as the weakest link. That link is usually publisher confirms, because a publisher without them looks completely healthy right up until the moment it does not.
Two details about confirms matter for RabbitMQ message durability. For a persistent message routed to a durable queue, RabbitMQ sends the confirm after persisting the message to disk — so the confirm means "written," not "received." And the broker batches those disk writes over short intervals to reduce fsync calls, which is exactly the window in which an unconfirmed message is at risk.
The publisher's obligation does not end at enabling confirms. It has to hold unconfirmed messages somewhere it can retransmit them from, and resend anything the broker never acknowledged once a connection recovers. Turning confirms on and ignoring the callbacks buys the overhead without the guarantee.
Why a durable queue alone does not prevent loss
Because durable and persistent describe different things, and the words are close enough to get swapped routinely.
Durable is a property of the queue, fixed at declaration. The queue definition — name, arguments, bindings — is written to the schema database and recreated when the node comes back. That is metadata, not data.
Persistent is a property of the message, set per publish via the delivery mode property: 2 for persistent, 1 for transient. It tells the broker to write the message body to disk.
Publish transient messages into a durable queue and they are dropped on recovery. Publish persistent messages into a non-durable queue and the queue disappears, taking them with it.
There is a third piece people forget: the exchange should be durable too. A transient exchange vanishes on restart and its bindings go with it, so a durable queue survives with nothing routing to it. Publishers keep publishing and messages go nowhere — worse than an error, because it is silent.
Do quorum queues improve RabbitMQ message durability?
Materially, yes — they remove the single-node failure case entirely.
Quorum queues are always durable; you cannot declare a non-durable one. They replicate through the Raft consensus algorithm, requiring agreement from a majority of members — (N/2)+1 where N is the group size. The default group size is 3, and an odd number is recommended so a majority is unambiguous. Set it explicitly with x-quorum-initial-group-size on larger clusters rather than relying on the default.
This matters more than it used to, because classic queues have no replication left. Classic queue mirroring was removed in RabbitMQ 4.0 after three years of deprecation, and classic queues are now a non-replicated queue type. On 4.x, an unreplicated classic queue holding anything you care about turns a single node loss into a data loss event. The migration from classic queues to quorum queues is the fix, and it is not a drop-in — quorum queues do not support exclusivity, and the operational profile differs.
What quorum queues do not do is repair the rest of the chain. Replication protects the message once the broker has accepted it. It does nothing about a publisher that never waited for a confirm, or a consumer that acknowledged before it finished. Teams migrate expecting RabbitMQ message durability to become someone else's problem, then lose messages at the same rate as before.
Quorum queues do add a useful poison-message control: x-delivery-limit, defaulting to 20. A message redelivered past that limit is dead-lettered or dropped instead of cycling forever. Classic queues have no equivalent, which is how requeue loops become incidents.
What are unacked messages and why do they matter?
An unacked message has been delivered to a consumer and not yet acknowledged. RabbitMQ holds it so it can be redelivered if that consumer dies. That is a durability feature — but a growing unacked count is one of the most reliable early signals that something is wrong.
Prefetch is the control. basic.qos limits how many deliveries can be in flight per channel; 0 means unlimited, which is the setting that causes trouble. RabbitMQ's guidance puts optimal throughput usually in the 100–300 range, and notes a prefetch of 1 significantly reduces throughput. Both extremes hurt differently:
- Unlimited prefetch hands a consumer every ready message at once. Memory grows on both sides, redelivery after a crash is enormous, and one slow consumer starves the others because the work is already allocated.
- Prefetch of 1 serialises everything and spends most of its time waiting on round trips.
On a Boomi-integrated RabbitMQ engagement we worked through exactly this shape. Listener blockage on the integration side correlated directly with unacknowledged messages piling up — consumers had work allocated and were not finishing it, so the broker had nothing to give anyone else. Tuning prefetch was part of the fix. So was reverting a frame_max that had been lowered from the 128 KiB default to 16 KiB, hurting efficiency for no benefit, and moving the client connection off direct node IPs onto an F5 VIP so failover actually worked. We also evaluated single active consumer (x-single-active-consumer) for the parts of the workload needing sequential processing — it keeps one consumer active at a time and fails over automatically, a cleaner answer than a prefetch of 1.
None of that was a broker defect. All of it looked like one from the application side.
Observability mattered as much as the tuning. We added Prometheus and OpenTelemetry instrumentation with Grafana dashboards, because "the queue is backing up" is not a diagnosis — the ready-versus-unacked split is what separates a producer problem from a consumer problem from an acknowledgement problem.
Where does dead-lettering lose messages?
Dead-lettering is usually introduced for reliability, which makes it an easy place to break RabbitMQ message durability without noticing.
A message is dead-lettered when it is rejected with basic.reject or basic.nack and not requeued, when its TTL expires, when the queue exceeds a length limit, or — on quorum queues — when it exceeds the delivery limit. Note what is absent: if an entire queue expires, its messages are not dead-lettered. They are simply gone.
The bigger trap is the default strategy. Dead-lettering is at-most-once by default: the message is removed from the source queue and republished to the dead-letter exchange without publisher confirms. RabbitMQ's documentation says this directly — dead-lettering in a clustered environment is not guaranteed to be safe, because the message is already gone from the original queue while the republish may not land.
Quorum queues can close that hole. Setting dead-letter-strategy to at-least-once makes the republish use internal publisher confirms. It needs two companions: overflow set to reject-publish, and a dead-letter-exchange actually configured. Applying one without the others gets you the old behaviour with extra configuration.
Configure the dead-letter exchange and routing key through a policy rather than x-dead-letter-exchange queue arguments where you can. Arguments are fixed at declaration and cannot change without redeploying the applications that declare them — a bad place to be mid-incident.
Does RabbitMQ support exactly-once delivery?
No, and any answer that says otherwise is describing something else.
RabbitMQ's reliability guide is unambiguous: acknowledgements guarantee at-least-once delivery, and without them you get at-most-once. Exactly-once is not on the menu. The reason is structural rather than an implementation gap — if the broker sends a confirmation and the network drops it before the producer sees it, the producer must either resend (a duplicate) or drop it (a loss). There is no third option in a distributed system with unreliable links.
The same applies downstream. A consumer can be handed a message that was previously delivered to another consumer, and redeliveries carry a redelivered flag set true. That flag is a hint, not proof you have seen it before.
So the correct design is at-least-once plus idempotent consumers. Give every message a stable business identifier at publish time and make processing safe to repeat: upsert instead of insert, check-then-act inside a transaction, or record processed IDs with a TTL. RabbitMQ's guidance prefers idempotent handling to explicit deduplication tables, because a dedupe table is another piece of state that fails independently.
That is the part most often skipped, and the part that completes RabbitMQ message durability at the application layer rather than the broker.
A durability checklist for production
Run through this before assuming the broker is at fault:
- Exchanges and queues declared durable, or quorum queues used, which are always durable.
- Delivery mode 2 on every message that matters. Check the client library — some default to transient.
- Publisher confirms enabled and handled, with a retransmit path for anything unconfirmed after a reconnect.
- Manual acknowledgement, acked after processing completes, never before.
- A prefetch value chosen deliberately, not left at unlimited and not set to 1 without a reason.
- A delivery limit on quorum queues so poison messages stop cycling.
- Dead-lettering configured with intent, at-least-once where the messages matter.
- Monitoring that separates ready from unacked, so you can tell producer problems from consumer problems.
- A supported broker version. Guarantees only hold on a version still receiving fixes — see RabbitMQ end-of-life and version support for where the lines fall.
That last point is less academic than it sounds. We have run customers through 3.13.10 → 3.13.15 → 3.13.18 purely to clear security vulnerabilities before attempting a major upgrade, and separately taken a payments customer to RabbitMQ 4.2 on quorum queues — deliberately skipping intermediate versions, using Kubernetes sidecar deployments and a staged migration to reach zero downtime. Both count as RabbitMQ message durability work, even though neither changes a queue argument.
Trace your durability chain with AceMQ
Losing messages and unable to prove where? The cause is usually one specific broken link rather than a general reliability problem, and it is findable. AceMQ runs structured RabbitMQ assessments that trace the full chain from publisher to consumer — see RabbitMQ consulting and support and our 24/7 messaging support and SLAs, or talk to an AceMQ engineer.
FAQ
Can RabbitMQ lose messages?
Yes, and the usual cause is an incomplete durability chain rather than a broker defect. A message survives only if the queue is durable, the message was published as persistent, the publisher waited for a confirm, and the consumer acknowledged after processing. Break any link and loss is possible.
Are RabbitMQ queues persistent by default?
No. Durability is set at declaration time, and even a durable queue discards transient messages on recovery. Quorum queues are the exception — they are always durable and cannot be declared otherwise.
Does a durable queue mean messages survive a restart?
Only the queue definition survives by itself. RabbitMQ's own wording is that durable queues are recovered on node boot including messages published as persistent, while transient messages are discarded during recovery even if they were in a durable queue.
What is the difference between durable and persistent in RabbitMQ?
Durable describes the queue and means its definition is recovered after a restart. Persistent describes the message, set with delivery mode 2, and means the broker writes it to disk. You need both, plus publisher confirms, before the message is genuinely safe.
Do quorum queues prevent message loss?
They remove the single-node failure case by replicating through Raft across a majority of members, which classic queues no longer do at all since mirroring was removed in RabbitMQ 4.0. They do not fix a missing publisher confirm or an unacknowledged consumer.
What are unacked messages in RabbitMQ?
Deliveries that have been sent to a consumer but not yet acknowledged. They are held by the broker so they can be redelivered if the consumer dies. A rising unacked count with a static ready count means consumers are receiving work and not finishing it.
Does RabbitMQ support exactly-once delivery?
No. RabbitMQ's reliability guide states plainly that acknowledgements guarantee at-least-once delivery, and that duplicates are possible because a confirmation can be lost before it reaches the producer. Build idempotent consumers instead of expecting exactly-once.
Can dead-lettering lose messages?
Yes. By default dead-lettering is at-most-once: the message is removed from the source queue and republished without publisher confirms, so it can be lost if the target is unavailable. Quorum queues can set dead-letter-strategy to at-least-once, which requires overflow set to reject-publish and a configured dead-letter exchange.