Skip to content
Knowledge Hub

RabbitMQ classic, quorum or stream: which queue type to choose? Intermediate

Estimated reading time: 6 min read

A new project has three flows: order confirmation emails (one worker per job, must survive losing a node), a temporary reply queue for an internal request, and an audit feed that several services need to reread. Using the same queue type for all three gets at least one requirement wrong.

RabbitMQ 4.x has classic, quorum and stream. They differ in storage mechanism, replication and how they are read, not three levels of “fast, medium, slow”.

Default: quorum queues for every durable queue holding jobs. The RabbitMQ docs describe quorum as the option when you need a replicated, highly available queue. Classic only for temporary queues or when you accept the risk of losing a node. Stream when you need to reread.

Loading diagram…

Feature Classic Quorum Stream
Read model Message deleted after ack Message deleted after ack Append-only log, read by offset, not deleted on read
Replication (4.x) No Yes (Raft, 3 replicas by default) Yes
Confirm sent when Message written to disk (persistent, durable queue) Replicated to a majority Reached a majority of replicas
Replay No No Yes, within the retention window
Exclusive / transient Yes No (always durable, not exclusive) No
Priority Yes Yes since 4.3 (0 to 31); x-max-priority is ignored No
Message TTL Yes Yes No (use retention)
Dead-letter Yes Yes, including at-least-once No
Poison message (x-delivery-limit) No Yes, default 20 No
Global QoS Yes No No
Keeps messages in RAM Partly (CQv2 loads from disk when needed) Never Never

Three points checked against the docs: classic is non-replicated since 4.0; quorum and stream both raise a channel error with global QoS; stream retention is evaluated per segment, and with no age or size set the log grows until the disk is full.

Quorum queue
x-queue-type = quorum
x-quorum-initial-group-size = 3 # default 3, should be odd
x-delivery-limit = 20 # default 20 since 4.0, -1 disables (not recommended)
policy dead-letter-strategy = at-least-once # with overflow=reject-publish
Stream
x-queue-type = stream
x-max-age = 7D # default: not set
x-max-length-bytes = ... # default: not set
x-stream-max-segment-size-bytes = 500000000 # default 500 MB

AMQP 0-9-1 consumers of a stream must use manual ack, set a prefetch, and pick a starting point with the x-stream-offset argument (if unset, reading starts from offsets written after attaching). The docs recommend a stream protocol client for the full feature set and the best throughput.

If the problem really is a partitioned log, very high throughput and a stream processing ecosystem, consider Kafka: see Kafka or RabbitMQ and Kafka distributed log.

Terminal window
npx --prefix examples ts-node examples/src/rabbitmq-queue-types/selection-model.ts

The script prints three suggestions: email job → quorum, audit feed → stream, temporary reply → classic. It is only a decision aid; it does not connect to RabbitMQ or verify replication.

Real verification needs a multi-node cluster. Quorum: publish persistent messages with confirms, stop one node, watch publish/consume; then lose a majority of nodes to see availability drop.

Stream: two consumers with separate offsets reread the same record, then watch retention delete old data. A single local broker does not prove failover.

Classic mirrored queues were removed in 4.0. Declaring a queue with the same name and a different x-queue-type is rejected as a property mismatch, so the type cannot be changed in place.

  1. List queues using a mirroring policy (ha-mode) and treat them as unreplicated after 4.0.
  2. Create a new quorum queue (new name) with the same bindings.
  3. Move producers to the new exchange/binding or dual publish; drain the old queue with the existing consumers.
  4. Remove dependencies on features quorum lacks: exclusive, transient, global QoS, server-named queues.
  5. Set x-delivery-limit, a DLX and at-least-once dead-lettering on the new queue.
  6. Idempotent consumers, because during cutover the same message may appear in both queues.

This list is a general procedure inferred from the verified behavior of queue declaration and quorum features, not checked against the official migration page; read the migration guide for the exact version you use.

Type Use when Do not use when
Classic Temporary queues, replies, need exclusive or legacy priority Important jobs that must survive losing a node
Quorum Durable work queues needing HA and poison handling Temporary queues, high churn, need exclusive
Stream Fan-out, audit, replay within retention Competing-consumer work queues, need DLX or per-message TTL
  1. Every durable work queue: quorum, odd number of nodes (at least 3).
  2. Publisher confirms and manual ack; see the three acknowledgment boundaries.
  3. Set x-delivery-limit and a DLX; do not let poison messages cycle.
  4. Streams: set x-max-age or x-max-length-bytes from day one, and monitor disk.
  5. Temporary queues: exclusive, not quorum.
  6. Declare topology with versioned code; changing the type means creating a new queue.
  7. Prepare backups and a runbook: replication does not protect against accidental deletion.

Self-check: (1) Can a durable classic queue survive permanently losing the node that stores it? (2) Why does an audit feed fit a stream better than quorum? (3) Why can you not change the x-queue-type of a running queue?