RabbitMQ

Running RabbitMQ on Kubernetes & OpenShift: A Deployment Playbook

Scott Sternloff

By Scott Sternloff, Senior Enterprise Architect

LinkedIn · Updated

K8sStatefulSet · 3 ReplicasKubernetes & OpenShift
Running RabbitMQ on Kubernetes or OpenShift is increasingly common, and increasingly the source of operational surprises for teams who assume Kubernetes will just handle the complexity. It doesn't — at least not without understanding how RabbitMQ's quorum mechanisms interact with Kubernetes pod lifecycle management.
This guide covers the key deployment patterns, the specific gotchas that cause production incidents, and how to structure your Kubernetes RabbitMQ deployment for reliable operation.

How do you deploy RabbitMQ on Kubernetes?

The standard approach is the RabbitMQ Cluster Operator, the official Kubernetes operator maintained by Broadcom. The operator handles:
  • Deploying RabbitMQ as a StatefulSet with proper pod naming and stable network identities
  • Managing PersistentVolumeClaims (PVCs) for each cluster node's data storage
  • Handling cluster membership, node discovery, and the Erlang cookie
  • Providing a RabbitmqCluster custom resource for declarative cluster configuration
For OpenShift, the same operator works with some additional SCC (Security Context Constraint) configuration. AceMQ also supports deployments on RKE2, AKS, ACK, and other managed Kubernetes variants.
Minimum cluster configuration: Three nodes, each on a separate Kubernetes worker node (enforced through pod anti-affinity rules). Running two RabbitMQ pods on the same worker node defeats high availability.

What is the termination grace period and why does it matter?

The RabbitMQ Cluster Operator includes a pre-stop hook with a default termination grace period of 604,800 seconds (one week). This is not a mistake — it's a safety mechanism.
Before a RabbitMQ pod shuts down, the pre-stop hook checks whether any queues currently mastered on that pod are in a quorum-critical state — meaning the pod's shutdown would bring one or more queues below quorum. If that condition is true, the pod refuses to terminate until quorum is restored.

"What the pre-stop hook does is it checks to make sure that none of the queues are in a quorum-critical status before it allows the pod to exit. It's protecting quorum. But when you go from three to zero replicas, it creates a deadlock — because taking one pod down makes quorum critical, which prevents that pod from terminating, which prevents the cluster from coming down."

Scott Sternloff, AceMQ Principal Architect, Adeptia engagement session, April 2026

How do you safely scale a RabbitMQ cluster to zero on Kubernetes?

The safe procedure for scaling down to zero requires two steps executed in sequence, not simultaneously:
Step 1: Reduce the termination grace period to 30 seconds:
kubectl patch rabbitmqcluster <cluster-name> -n <namespace> \
  --type merge \
  -p '{"spec": {"terminationGracePeriodSeconds": 30}}'
Step 2: After that change takes effect, scale replicas to zero:
kubectl patch rabbitmqcluster <cluster-name> -n <namespace> \
  --type merge \
  -p '{"spec": {"replicas": 0}}'
When you bring the cluster back up, restore the grace period to its default:
kubectl patch rabbitmqcluster <cluster-name> -n <namespace> \
  --type merge \
  -p '{"spec": {"replicas": 3, "terminationGracePeriodSeconds": 604800}}'

What happens when a Kubernetes node gets drained during a cluster upgrade?

When a Kubernetes cluster upgrade drains a node, pods on that node are terminated. If a RabbitMQ pod is running on the drained node and its queues are quorum-critical, the pod will get stuck in terminating state — blocking the node drain, blocking the upgrade, and requiring manual intervention.

"Your clients have an automated process to upgrade their cluster. At a time, one node can go down. And because the RabbitMQ pod running on that node refuses to terminate due to quorum protection, it won't allow the node to be drained. Their upgrade process gets stuck."

Scott Sternloff, AceMQ Principal Architect, Adeptia session, April 2026

Solutions:
  1. Reduce the default termination grace period before automated upgrade windows, and restore it afterward
  2. Set a watchdog timer: if a pod hasn't terminated within N seconds of receiving a SIGTERM, force-delete it (use cautiously)
  3. Use pod disruption budgets (PDBs): a PDB set to allow at most one unavailable pod at a time ensures Kubernetes respects quorum constraints during node drains

What should you never do with RabbitMQ pods on Kubernetes?

Never force-delete a RabbitMQ pod without understanding the quorum impact. Force-deleting (kubectl delete pod --force --grace-period=0) bypasses the pre-stop hook entirely. If the pod being force-deleted is the quorum leader for any queue, those queues lose quorum immediately.
Never run two RabbitMQ pods on the same Kubernetes node. Anti-affinity rules should enforce this automatically, but verify your anti-affinity configuration is correctly set.
Never scale from 3 replicas to a lower number without first verifying quorum health.
kubectl exec -n <namespace> <rabbitmq-pod> -- rabbitmq-diagnostics check_running
kubectl exec -n <namespace> <rabbitmq-pod> -- rabbitmq-queues check_if_node_is_quorum_critical
If any queues report as quorum-critical, wait for them to recover before proceeding with the scale-down.

Storage and monitoring considerations

Each RabbitMQ pod requires a PersistentVolumeClaim. For production:
  • Use a StorageClass with volumeBindingMode: WaitForFirstConsumer to ensure pods and their volumes land on the same availability zone
  • SSD or NVMe storage is strongly recommended — quorum queue WAL writes are latency-sensitive
  • Set retention policy to Retain on PVCs so that data survives pod deletion
  • Do not use shared storage (NFS, shared file systems) for RabbitMQ data volumes
Key metrics to track in Kubernetes-specific deployments:
  • rabbitmq_quorum_queue_stat_voters — verify quorum is maintained across nodes
  • Pod restart counts — frequent restarts indicate quorum or resource issues
  • PVC capacity — monitor disk utilization on each pod's PVC
  • rabbitmq_node_disk_free — alert before the disk alarm triggers
If you're planning a new deployment, migrating an existing cluster to Kubernetes, or dealing with upgrade-related issues, contact AceMQ support for deployment architecture or hands-on support.

Base images are a recurring supply-chain question in these deployments — see Bitnami image security and the enterprise alternatives.

RabbitMQ on Kubernetes or virtual machines: how to decide

RabbitMQ runs well on both. The deciding factors are who operates the platform and what the storage underneath looks like.

Who runs it. If a platform team already runs stateful workloads on Kubernetes, with an operator, PodDisruptionBudgets and StatefulSet upgrades as daily routine, RabbitMQ is one more StatefulSet. If your infrastructure team runs VMs well and Kubernetes is only where the stateless apps live, putting the broker there means the messaging team learns Kubernetes failure modes on a production system.

Storage. Quorum queues and streams fsync on every batch of writes. On a VM with local NVMe that is sub-millisecond. On Kubernetes the default is a network-attached PersistentVolume, and every fsync crosses the storage network. We typically see 2 to 5 ms per fsync on cloud block storage, which shows up directly as publisher confirm latency on quorum queues. Local PV provisioners exist but pin the pod to a node. Classic vs quorum queues covers why quorum queues care about this more than classic queues do.

Upgrades. Kubernetes does a rolling StatefulSet restart, one pod at a time, with the operator waiting for quorum before each step. On VMs you upgrade in place, node by node, on your own schedule. The Kubernetes version is more automated and less forgiving of a broken assumption.

Failure domains. On Kubernetes, nodes get drained, pods get evicted and the autoscaler reclaims capacity without asking you. A PDB and pod anti-affinity are the only things between a routine drain and a lost quorum. VMs fail less often and on schedules you control, but they fail harder, with no scheduler to restart the broker somewhere else.

Networking. Pods get hostnames from the headless service and IPs that change on every restart. Clients connect through a Service or ingress and must reconnect cleanly, because they will be reconnected. Anything that pins a broker IP breaks. On VMs, hostnames stay stable for years and client reconnect bugs stay hidden.

Licensing. Tanzu RabbitMQ, the commercial distribution, is packaged first for Kubernetes, with a VM image alongside. Open-source RabbitMQ runs anywhere.

Our recommendation: VMs when the team is small and latency-sensitive quorum queue workloads dominate. Kubernetes when there is a platform team, an operator in use, and the rest of the estate already lives there. Across our engagements the split is roughly even, and the Kubernetes deployments that go badly are almost always the ones adopted because Kubernetes was available, not because anyone was ready to operate stateful workloads on it.

Operator, Helm chart, or hand-rolled StatefulSet

The RabbitMQ Cluster Kubernetes Operator is the upstream-maintained option and the one we default to. You declare a RabbitmqCluster resource and the operator creates the StatefulSet, the headless service for peer discovery, the client Service, the Erlang cookie and default user Secrets, and the server configuration. It handles rolling restarts with a preStop hook that waits for quorum-plus-one before taking a node down, and it wires TLS from a Secret you point it at. It does not create a PodDisruptionBudget on its own; add one with maxUnavailable: 1. Alongside it, the Messaging Topology Operator turns users, vhosts, permissions, queues, exchanges, bindings, policies, shovels and federation upstreams into CRDs, so topology lives in Git next to the cluster definition.

A minimal production-shaped cluster:

apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
  name: orders
spec:
  replicas: 3
  resources:
    requests:
      cpu: "2"
      memory: 8Gi
    limits:
      cpu: "2"
      memory: 8Gi
  persistence:
    storageClassName: fast-ssd
    storage: 100Gi
  rabbitmq:
    additionalConfig: |
      cluster_partition_handling = pause_minority
      vm_memory_high_watermark.relative = 0.6
      disk_free_limit.absolute = 5GB

Keep requests equal to limits. The operator derives RabbitMQ's memory override from the pod's memory setting, so the high watermark and the cgroup limit agree. Add spec.affinity with pod anti-affinity across nodes, or zones if you have them.

Community Helm charts template a StatefulSet plus the services, config and secrets in one helm install. They are fine for getting a cluster up. They do nothing after install: no ordered rolling upgrade that waits for quorum, no topology management, and every helm upgrade rewrites the StatefulSet and hopes. If you standardise on Helm for everything, run the operator and manage the RabbitmqCluster resource with a chart instead.

A hand-rolled StatefulSet is defensible in two cases: a cluster where you are not allowed to install CRDs or cluster-scoped operators, or a team that already has a hardened StatefulSet pattern for other stateful systems and wants RabbitMQ to match it. What you take on is everything above: peer discovery configuration, the preStop drain hook, probe tuning, secret rotation and the upgrade choreography. The recurring bug we inherit is an upgrade that restarted two pods at once.

The Kubernetes failure modes we get called about

A drain took the quorum leader, and the PDB let it. Symptom: publishers block on confirms for 10 to 30 seconds during a routine node upgrade, or a queue reports no leader because two of three replicas are down. Cause: no PDB, or one that allows more than one pod unavailable, combined with a cluster upgrade that drains nodes faster than quorum queues re-elect and catch up. Fix: a PDB with maxUnavailable: 1, pod anti-affinity so replicas never share a node, and the operator's preStop hook (or your own rabbitmq-upgrade await_online_quorum_plus_one) so the drain waits.

PV reattach after a node failure. Symptom: after a node dies, its RabbitMQ pod sits in ContainerCreating with a Multi-Attach error or a volume node affinity conflict for six minutes or more. Cause: zone-bound block storage can only attach in its own zone, and the dead node's attachment is not released until the node controller gives up on it. Fix: the cluster stays up on two of three replicas if anti-affinity spread them, so do not fight it. Use a WaitForFirstConsumer storage class so the volume lands in the pod's zone, and treat the reattach window as a known cost of network storage.

OOM-killed before flow control. Symptom: a pod restarts with exit code 137 and no RabbitMQ error in the log; the memory alarm never fired. Cause: the container memory limit sits below the point where the high watermark would engage, either because RabbitMQ read host memory instead of the cgroup limit, or because the watermark leaves no headroom for the Erlang VM's allocators and binary heap. Fix: requests equal to limits, a watermark that leaves 20 to 30 percent below the limit, and rabbitmq-diagnostics memory_breakdown to confirm the total RabbitMQ sees matches the pod.

Liveness probe restarts during recovery. Symptom: after an unclean restart a node with large queues goes into CrashLoopBackOff, each attempt getting further through the index rebuild before the kubelet kills it. Cause: a liveness probe running rabbitmq-diagnostics check_running or similar fails while the node replays Mnesia or Khepri and rebuilds queue indexes, which on a big node takes longer than the probe's failure threshold. Fix: a startup probe with a generous failureThreshold, a readiness probe on port 5672 only, and no aggressive liveness probe at all. The operator ships without a liveness probe for exactly this reason.

A DNS or headless service change broke peer discovery. Symptom: pods start but each forms a cluster of one, or refuse to boot because the data directory belongs to a node with a different name. Cause: node names derive from the pod hostname and the headless service, so renaming the service, the namespace or the cluster DNS domain changes every node name and orphans the on-disk data. Fix: do not rename. If a rename is unavoidable, treat it as a migration to a new cluster: export definitions, shovel or drain the queues, and cut clients over.

For the wider list of what breaks when messaging lands on Kubernetes, see what breaks with messaging on Kubernetes. For what to watch so these arrive as alerts rather than tickets, see RabbitMQ Kubernetes monitoring. If you would rather have someone who has fixed these before, see our Kubernetes and containers consulting and RabbitMQ support.

The full sequence — whether Kubernetes is the right home, the Operator, storage classes, network policy, requests and limits, monitoring and rolling updates — is in the RabbitMQ on Kubernetes guide.

FAQ

Should RabbitMQ run on Kubernetes or on virtual machines?

Both work. Run it on VMs when the team is small, the workload leans on quorum queues or streams with tight publisher confirm latency, and you do not already operate stateful workloads on Kubernetes. Run it on Kubernetes when there is a platform team, you use the Cluster Operator, and the applications that talk to it already live there. Storage is the question that bites: network-attached volumes add milliseconds to every fsync, and quorum queues fsync constantly. We run both for clients. Neither is wrong on its own; picking Kubernetes without anyone ready to operate it is.

Which RabbitMQ Kubernetes operator should we use?

The RabbitMQ Cluster Kubernetes Operator, maintained upstream by the RabbitMQ team, paired with the Messaging Topology Operator. The Cluster Operator manages the RabbitmqCluster resource: StatefulSet, services, secrets, configuration, TLS and quorum-aware rolling restarts. The Topology Operator manages users, vhosts, queues, exchanges, bindings and policies as Kubernetes resources so topology lives in Git. Add your own PodDisruptionBudget. Helm charts are fine for a first install but manage nothing after it. A hand-rolled StatefulSet only makes sense where CRDs are not allowed.

Does RabbitMQ run well on OpenShift?

Yes. We run RabbitMQ on OpenShift for enterprise clients alongside EKS, AKS and GKE. The Cluster Operator installs from OperatorHub, and its pods run under the restricted SCC once the pod security context is left to OpenShift rather than set by the operator, which spec.override supports. What differs from vanilla Kubernetes is Routes instead of Ingress for AMQP over TLS, SCC-driven volume permissions, and OpenShift's own node upgrade cadence, which makes the PDB and anti-affinity guidance above non-negotiable rather than nice to have.

How many RabbitMQ replicas should a Kubernetes cluster have?

Three, spread across nodes and ideally zones with pod anti-affinity. Three replicas tolerate one loss while quorum queues keep a majority, and one loss is what a drain, a node failure or an upgrade step produces. Five is worth it only when you must survive a zone outage and a maintenance event at the same time. Never two: quorum queues need a majority and two nodes lose it on any single failure. Throughput scaling is a separate question from replica count, and HA, DR and cluster sizing covers sizing.

Free Consultation

Get Expert Eyes on Your RabbitMQ Cluster

Whether you're troubleshooting a production incident, planning a migration, or want a second opinion on your architecture — our team is ready. No pitch, just answers.

Email Us