Publisher Confirms vs Transactions in RabbitMQ: Which Guarantees Do You Need?
Publisher Confirms vs Transactions in RabbitMQ – a practical guide to choosing the right delivery guarantee for your high‑throughput service. Understand the trade‑offs, see a real‑world example, and get a quick checklist to deploy safely.
21 Jul 2026, 15:21 UTC

When Do You Need Exactly‑Once Delivery?
In many micro‑service scenarios you want to guarantee that a message you publish will either reach the broker or you will be notified that it didn’t. The two built‑in mechanisms that RabbitMQ offers for this are Publisher Confirms and Transactions. Both are channel‑level features that change the semantics of basic.publish, but they differ dramatically in performance, complexity, and the guarantees they provide.
Publisher Confirms: Lightweight, Asynchronous Acknowledgment
When a channel is put into confirm mode with confirm.select, the broker acknowledges each message after it has been persisted (or after it has been routed to a queue if the message is transient). Confirmations are sent asynchronously, so the publisher does not block on each basic.publish. The client typically correlates acks/nacks to the sequence number of the publish. A common pattern is to batch a set of N messages, wait for an ack for that batch, and then send the next batch.
Typical throughput impact is modest: benchmarks on a modern RabbitMQ 3.8+ cluster show a 5–10 % drop compared to unconfirmed publishes. The reason is that confirms are pipelined – the broker can process many publishes before sending back a single ack packet.
Because confirms are per‑message, a nack only affects that one message. If you retry a message after a timeout, you must be prepared for the broker to have already persisted it, which can lead to duplicates. Idempotent consumers or a deduplication store keyed on message‑id or a custom header are the usual mitigation.
Transactions: Heavyweight, Atomic Commit
Transactions wrap a group of publishes in a single atomic unit. You call tx.select, publish one or more messages, then tx.commit (or tx.rollback). The broker flushes the transaction to disk and blocks the channel until the commit finishes. Each commit forces an fsync on the broker node, which introduces a 10–50× slowdown compared to confirms.
Transactions guarantee that either all messages in the transaction are stored or none are. They do not protect against a client retrying after a timeout – the retry will simply be a new transaction. Also, transactions are confined to a single channel; you cannot roll back a publish from a different channel or connection.
Choosing the Right Tool: A Decision Flow
- Do you need atomicity across multiple queues or exchanges? If yes, use a transaction. If no, skip this step.
- What throughput do you require? If you need >5 k msg/s, confirm mode is almost always preferable because of its low overhead.
- Do you have idempotent consumers or a deduplication strategy? If not, a transaction can reduce duplicate risk for a small batch, but the performance penalty may outweigh the benefit.
- Are you using quorum queues? Confirm latency will include replication to the quorum, which may add a few milliseconds but gives stronger durability guarantees.
- Are you using lazy queues? Confirm latency will reflect disk writes; monitor the
messages_readyvsmessages_unackedcounts.
Worked Example: High‑Throughput Order Service
Suppose an order service publishes 10 k messages per second to a durable exchange. Two options were compared on a 3.10.0 broker:
- Confirms – 100‑message batch, 30 s client‑side timeout,
mandatory=trueenabled. Result: ~9.5 k msg/s,Publish confirm latency~2 ms on average. - Transactions – one commit per 100 messages. Result: ~0.5 k msg/s,
fsynclatency dominates.
The team chose confirms with an idempotent consumer that uses the message‑id header to detect duplicates. This configuration kept throughput high while still providing reliable delivery.
Practical Checklist Before You Deploy
- Verify your client library supports confirm batching. In Java:
channel.confirmSelect(); channel.basicPublish(exchange, routingKey, props, body); - For transactions, ensure you call
txSelectbefore any publish andtxCommitafter the batch.channel.txSelect(); channel.basicPublish(...); channel.txCommit(); - Check that you’re not mixing confirms and transactions on the same channel – RabbitMQ will reject the second mode change.
- Use
rabbitmqctl list_queues name messages_ready messages_unackedto monitor unacked counts while publishing. A sudden spike indicates ack delays. - Enable the management plugin and watch the
Publish confirm rateandPublish confirm latencymetrics under the Overview tab. - If using quorum queues, remember that confirms are only sent after the message is replicated to the quorum. This adds a few milliseconds of latency but gives stronger durability.
- Test nack handling: publish to a non‑existent exchange with
mandatory=trueand confirm mode enabled. Your client should receive abasic.nack.
Conclusion
For most high‑throughput services, Publisher Confirms are the right choice: they offer near‑native throughput, simple semantics, and are supported by all major client libraries. Use them with an idempotent consumer or a deduplication store to avoid duplicates.
Reserve Transactions for rare cases where you truly need atomicity across multiple queues or exchanges, and where the performance hit is acceptable. Always benchmark against your workload and monitor confirm latency, especially if you run quorum or lazy queues.
By following the decision flow and checklist above, you can pick the mechanism that balances performance with the delivery guarantees your application demands.