RabbitMQ Performance Tuning
A default RabbitMQ installation works well for development but often needs tuning for high-throughput production workloads. Performance tuning covers the operating system, broker configuration, queue design, client behaviour, and hardware choices. This topic covers each layer with specific settings and their effects.
Measuring Before Tuning
Always measure first. Use the Management UI, the HTTP API, or PerfTest (the official RabbitMQ benchmarking tool) to establish a baseline before changing anything. PerfTest lets you simulate realistic producer and consumer workloads:
# Install PerfTest wget https://github.com/rabbitmq/rabbitmq-perf-test/releases/latest/download/perf-test.jar # Run a basic benchmark: 1 producer, 1 consumer, auto-ack java -jar perf-test.jar --uri amqp://guest:guest@localhost # More realistic: 5 producers, 10 consumers, persistent messages, manual ack java -jar perf-test.jar \ --uri amqp://localhost \ --producers 5 \ --consumers 10 \ --flag persistent \ --ack-mode manual \ --qos 5 \ --size 1024
Operating System Tuning
File Descriptor Limit
Each TCP connection and open file uses one file descriptor. The default Linux limit (1024) is far too low for a production broker. Set it to at least 65536 per process:
# /etc/systemd/system/rabbitmq-server.service.d/limits.conf [Service] LimitNOFILE=65536
# Or in /etc/security/limits.conf rabbitmq soft nofile 65536 rabbitmq hard nofile 65536
TCP Buffer Sizes
Increase the kernel's TCP send and receive buffer sizes for high-throughput network connections:
# /etc/sysctl.conf net.core.rmem_max = 134217728 net.core.wmem_max = 134217728 net.ipv4.tcp_rmem = 4096 87380 134217728 net.ipv4.tcp_wmem = 4096 65536 134217728
sysctl -p # apply changes without reboot
Swap Space
RabbitMQ performs very poorly when the OS starts using swap. Either provide enough RAM or use Linux's vm.swappiness to make the kernel avoid swap:
echo 'vm.swappiness=10' >> /etc/sysctl.conf sysctl -p
RabbitMQ Broker Configuration Tuning
Memory High Watermark
The default watermark is 40% of system RAM. Raise it if your server has plenty of RAM and you want to use more before flow control kicks in:
# rabbitmq.conf vm_memory_high_watermark.relative = 0.6 # allow up to 60% RAM usage
Disk Free Limit
disk_free_limit.absolute = 2GB # block publishers if disk falls below 2 GB free
Heartbeat Interval
The heartbeat detects dead connections. A shorter heartbeat detects stale connections faster and frees resources sooner:
heartbeat = 60 # seconds (default is 60)
TCP Listener Settings
tcp_listen_options.backlog = 4096 tcp_listen_options.nodelay = true tcp_listen_options.sndbuf = 196608 tcp_listen_options.recbuf = 196608 tcp_listen_options.keepalive = true
Queue Design for Performance
Keep Queues Short
RabbitMQ performs best when queues are short (under ~10,000 messages). A long queue causes RabbitMQ to page messages to disk, which dramatically reduces throughput. Increase consumer count rather than letting queues grow deep.
Use Quorum Queues for Reliability, Classic Queues for Speed
Classic transient queues (non-durable, in-memory messages) are the fastest queue type. Quorum queues prioritise safety over raw speed. Classic durable queues fall in between. Choose based on your reliability requirements:
Fastest: Classic queue, transient messages (no disk writes) Balanced: Classic queue, persistent messages (disk write per message) Safest: Quorum queue (Raft replication, disk write before ack)
Avoid Many Small Queues
Ten thousand queues with one message each is harder to manage than ten queues with a thousand messages each. Each queue is an Erlang process with overhead. Design your topology to use fewer, well-named queues.
Client-Side Tuning
Batch Publishes with Publisher Confirms
Instead of confirming each message individually, publish a batch of 100–500 messages and wait for a batch confirm. This amortises the network round-trip cost:
BATCH_SIZE = 200
channel.confirm_delivery()
for i, msg in enumerate(messages):
channel.basic_publish(exchange='', routing_key='q', body=msg)
if (i + 1) % BATCH_SIZE == 0:
channel.wait_for_confirms_or_die()
Optimise Prefetch Count
A prefetch of 1 maximises fairness but minimises throughput (one network round-trip per message). For fast, uniform tasks, increasing prefetch to 10–100 significantly improves throughput:
channel.basic_qos(prefetch_count=50) # experiment to find the sweet spot
Reuse Connections and Channels
Opening a new TCP connection per publish is expensive. Open one connection per process and create channels within it. Opening and closing channels per operation is also wasteful — keep long-lived channels for consumers.
Compress Large Messages
For messages over ~10 KB, gzip compression reduces network bandwidth significantly. Compress in the producer and decompress in the consumer:
import gzip, pika
body = gzip.compress(large_json_string.encode())
channel.basic_publish(
exchange='', routing_key='q', body=body,
properties=pika.BasicProperties(content_encoding='gzip')
)
Hardware Recommendations
Component Recommendation ------------------------------------------- RAM 16 GB minimum, 64 GB for high-volume brokers CPU 4–8 cores (RabbitMQ uses multiple cores for connections and channels) Disk SSD is essential for persistent messages; SATA SSD minimum, NVMe ideal Network 1 Gbps minimum, 10 Gbps for high-throughput clusters OS Linux (Ubuntu 22.04 or RHEL 8+) — best performance and support
Monitoring Performance Continuously
Enable the Prometheus plugin and build a Grafana dashboard. Track these metrics continuously:
- Publish rate vs delivery rate (they should be balanced)
- Queue depth trend (should be flat or declining)
- Memory usage (stay below 50% of watermark)
- Disk I/O throughput and latency (spikes indicate disk bottleneck)
- Connection and channel count (watch for leaks)
Performance Tuning Checklist
- Set OS file descriptor limit to 65536+
- Increase TCP buffer sizes via sysctl
- Disable swap or set swappiness to 10
- Set memory watermark to 0.6 if server has abundant RAM
- Use SSDs for the RabbitMQ data directory
- Keep queue depths short — add consumers before queues grow
- Batch publishes and use publisher confirms in batch mode
- Tune prefetch count for the right balance of fairness vs throughput
- Reuse connections and channels across publishes
- Compress messages larger than 10 KB
- Monitor continuously with Prometheus and Grafana
Summary
RabbitMQ performance tuning works at multiple layers. The OS needs higher file descriptor limits and larger TCP buffers. The broker needs appropriate memory watermarks and TCP listener settings. Queue design should favour shallow, well-named queues over deep, many queues. Clients should batch publishes, tune prefetch counts, reuse connections, and compress large messages. Use SSDs for persistent message storage. Establish a Prometheus and Grafana monitoring stack to detect performance regressions early. Measure before and after every change to confirm the tuning actually helps.
