RabbitMQ classic mirrored queues removed: migrating to quorum queues

If you upgraded RabbitMQ and your ha-mode policies silently stopped doing anything, that is not a bug. Classic queue mirroring was deprecated in 3.9 and removed in 4.0. On 4.x the mirroring-related keys in your policies (ha-mode, ha-params, ha-sync-mode, ha-promote-on-shutdown, ha-promote-on-failure, ha-sync-batch-size) have no effect. The classic queues themselves keep working, but each one now runs as a single, non-replicated queue on one node.

That is the dangerous part: nothing crashes, nothing logs an obvious error, and producers and consumers keep flowing. You simply lost replication. The first time you notice is often the first node failure after the upgrade, when queues you believed were highly available go down with their host node.

This article covers getting from that state to quorum queues: how to find what is affected, which features do not carry over, how to run the migration, and which new failure modes to monitor afterward.

Why ha-mode policies silently stop meaning anything

Policies are still supported in RabbitMQ 4.x. What changed is the set of keys the server acts on. A policy matching classic queues that only contains ha-mode and friends still matches queues but changes nothing about them. The result:

  • Queue state: classic queues show running, keep serving traffic, and report normally in the Management API. There is no “mirroring broken” state because mirroring no longer exists as a concept.
  • Node failure behavior: a classic queue mastered on a failed node shows down until the node returns. Before the upgrade, a mirror on a surviving node would have been promoted. Now there is nothing to promote.
  • Upgrade behavior on 4.0: classic queues still on the v1 storage implementation are converted to v2 during startup, which can take a long time on nodes holding many messages. If you still have classic_queue.default_version = 1 in rabbitmq.conf, the node refuses to start. Pre-convert deliberately on 3.13 before upgrading.

Two more defaults changed in 4.0 that interact with this migration: quorum queues gained a default redelivery limit of 20 (messages redelivered more than that are dead-lettered or dropped), and the default maximum message size dropped to 16 MiB.

Quorum queues vs classic mirrored queues

AspectClassic mirrored queueQuorum queue
ReplicationBespoke master/mirror protocolRaft consensus, leader plus followers
DeclarationAny classic queue + ha-mode policyx-queue-type=quorum at declaration (or via default_queue_type)
DurabilityOptional (transient queues allowed)Always durable, per-message
Poison message handlingRequeue loop by defaultx-delivery-limit, default 20 in 4.x, then dead-letter or drop
Memory per messageLowerHigher (Raft metadata and WAL overhead)
Disk footprintMessage store + indexPer-queue Raft WAL segments, compacted via snapshots
Node failureMirror promotion, known consistency gaps across partitionsLeader election; minority side becomes read-only or unavailable
Minimum sensible cluster2 nodes3 nodes (no HA benefit below 3 members)

The failure-mode shift is what operators underestimate. Mirrored queues failed toward availability with possible divergence. Quorum queues fail toward consistency: a queue that loses a majority of its members shows state minority and stops accepting writes while still serving already-committed messages. Your alerting needs to know the difference.

Inventory: find what is affected

Before touching anything, build the list of queues and policies that need attention. These are safe, read-only checks.

# List all policies and their definitions
rabbitmqctl list_policies

# List every queue with its type, leader node, and member nodes
rabbitmqctl list_queues name durable type leader online members

# Count leaders per node (leader imbalance check, useful before and after migration)
rabbitmqctl list_queues name type leader | awk '{print $3}' | sort | uniq -c

What you are looking for:

  • Policies with ha-mode keys: every policy whose definition contains ha-mode is a migration candidate. On 3.10.13/3.11.5 and later you can inspect effective_policy_definition per queue via the Management API to see exactly which policy applies to which queue.
  • Classic queues that should be replicated: any durable classic queue that carried an ha-mode policy must become a quorum queue.
  • Queues that must stay classic: transient (non-durable) queues cannot be quorum queues. They remain classic, non-mirrored. Accept the durability semantics or redesign them as durable.
  • Exclusive queues: cannot be declared as quorum. Attempting it returns an error. They stay classic by design.
flowchart TD
  A[Queue under ha-mode policy] --> B{Durable?}
  B -->|No| C[Stays classic, non-mirrored]
  B -->|Yes| D{Exclusive?}
  D -->|Yes| C
  D -->|No| E{Uses incompatible features?}
  E -->|reject-publish-dlx, global QoS, priorities pre-4.0| F[Remediate app or policy first]
  E -->|None| G[Migrate to quorum queue]
  F --> G

Feature compatibility checklist

Work through this table per queue before migrating. These are the incompatibilities that break applications if you migrate blind.

FeatureClassic mirroredQuorum queueRequired action
reject-publish-dlx overflowSupportedNot supportedChange policy to reject-publish, handle dead-lettering in the application
Global QoS prefetchSupportedNot supportedMove to per-consumer QoS
Message prioritiesSupportedNot supported before 4.0; 4.0 adds priorities with different semanticsSplit into multiple queues or verify 4.x behavior against your workload
x-delivery-limitNot applicableSupported; default 20 in 4.xEnsure a dead-letter exchange is configured, or raise the limit via policy for legitimate high-redelivery workloads
queue_master_locatorSupportedDeprecated, replaced by queue_leader_locatorUpdate policies to the new key
Transient queuesSupportedNot supported (quorum queues are always durable)Keep as classic non-mirrored
Cluster size2 nodes usableNo benefit below 3 membersAdd a third node before migrating

One more operational trap: if you declare many quorum queues over a single connection, the node hosting that connection tends to become a member of every queue. Spread declarations across connections, or rebalance afterward.

Migration procedure

The safe pattern for queues holding in-flight messages is blue-green: stand up the new quorum queue, move producers, drain the old queue, delete it. In-place delete-and-recreate is only acceptable for queues that are empty or whose messages are disposable.

  1. Remediate incompatibilities. Fix every item flagged in the checklist above: overflow mode, global QoS, priorities, delivery limits. Do this on the classic queues first where possible, so the application change is decoupled from the queue-type change.
  2. Declare the new quorum queue. Same exchange bindings, x-queue-type=quorum, durable. Use a temporary name if you plan to keep the original name (blue-green), or declare after deleting the old queue (in-place).
  3. Move producers. Repoint publishers at the new queue (via binding or name change). Verify publish rate on the new queue and zero publish rate on the old one.
  4. Drain the old queue. Let consumers finish the backlog. For messages that cannot be replayed, use shovel or federation to move them to the new queue. Watch the old queue’s messages_ready reach zero.
  5. Delete the old queue. This is destructive: confirm messages_ready and messages_unacknowledged are both zero first. In blue-green with a renamed queue, rebind the original name now.
  6. Drop dead policies. Remove or rewrite ha-mode policies so they no longer pretend to do something. On 4.x, attempting to create a policy with ha-mode keys is refused; on 3.13 it still applies and masks queues you missed.
  7. Rebalance leadership. After migrating many queues, check leader distribution and run rabbitmq-queues rebalance all during low traffic. Leader transfer causes brief per-queue unavailability.

RabbitMQ’s rabbitmqadmin v2 CLI supports automated migration via the prepare_for_quorum_queue_migration and drop_empty_policies transformations, which generate much of this for you.

If you are still on 3.13 and planning the 4.0 upgrade: set default_queue_type (available since 3.13.3) so newly declared queues default to quorum, and convert any remaining CQv1 classic queues to v2 deliberately before the upgrade rather than during it.

After the migration: the failure modes change

Your runbooks and alerts written for mirrored queues need updating:

  • Leader elections are normal. Rolling restarts now trigger Raft elections. Expect them; alert only on flapping.
  • minority is a real state. A quorum queue with insufficient members cannot accept writes. Monitor queue state and treat sustained minority on an active queue as urgent.
  • Redundancy math changed. A 3-member quorum queue with one member down still commits (2 of 3) but has zero remaining fault tolerance. It looks healthy and is not. Watch the online member list per queue.
  • Disk profile changed. Quorum queues write per-queue WAL segments. Under sustained throughput, WAL growth can outpace snapshot compaction, especially if a follower is lagging. Watch disk free and the Raft WAL directory, not just the message store.
  • Memory profile changed. Quorum queues carry higher per-message memory overhead than classic queues. Re-baseline your mem_used / mem_limit expectations after migrating large queues.
  • Before any shutdown, run rabbitmq-diagnostics check_if_node_is_quorum_critical. If the node is quorum-critical, stopping it takes queues into minority.

Signals to monitor

SignalWhy it mattersWarning sign
Queue state per queueCatches minority, down, crashed after migration and during restartsAny active quorum queue in minority
leader and online members per queueReveals redundancy loss that looks healthyFewer online members than declared, or leaders concentrated on one node
Leader distribution across nodesThe leader does all publish and dispatch workOne node holding >60% of quorum queue leaders
Disk free vs disk_free_limitWAL growth is faster and spikier than classic store growthSustained downward trend; disk_free < 3x limit
mem_used / mem_limitHigher per-message overhead after migrationRatio trending above your pre-migration baseline
Redeliver rate per queueWith the default delivery-limit of 20, poison loops now end in dead-lettering or dropsElevated redeliver plus a growing DLX queue
Dead-letter queue depthWhere redelivery-limited messages landGrowing DLX queue nobody consumes from

How Netdata helps

  • Per-queue state and depth over time: Netdata’s RabbitMQ collector tracks queue state, messages_ready, and messages_unacknowledged per queue, so you can watch the old classic queue drain to zero while the new quorum queue takes over during a blue-green migration.
  • Leader and member visibility: queue type and node attribution in per-queue metrics let you spot a quorum queue that lost members while still serving traffic, the silent redundancy-loss case.
  • Disk and memory correlation: correlating disk free, memory ratio, and per-queue depth on the same dashboard separates classic store growth from post-migration WAL growth.
  • Redeliver and DLX signals: per-queue redeliver rates surfaced against queue depth make the new delivery-limit behavior visible before messages start landing in the dead-letter queue unnoticed.
  • Node-level alarms: memory and disk alarm states per node, correlated with publish rates, catch the cluster-wide publishing halt a WAL-driven disk alarm would cause.