ActiveMQ Capacity Planning: The Complete Framework

meshIQ July 14, 2026

Most ActiveMQ deployments are sized in one of two ways: either under-provisioned from underestimating growth ("we'll upgrade when we need to") or over-provisioned from anxiety ("better give it 32GB just in case"). Both approaches are avoidable with a structured capacity planning framework that translates your messaging workload characteristics into specific hardware and configuration requirements.

This guide provides the ActiveMQ capacity planning framework: the five workload dimensions that determine broker sizing, specific formulas for JVM heap, disk capacity, and IOPS requirements, connection and destination scaling limits, the built-in benchmark tool for validating storage, capacity trigger thresholds that define when expansion is needed, and a horizontal vs. vertical scaling decision framework.

The Five Capacity Dimensions

ActiveMQ broker capacity is determined by five workload dimensions. Before sizing any resource, such as heap, disk, CPU, or connections, these five dimensions must be characterized:

Dimension 1: Message Throughput

Throughput is measured in messages per second (msg/s) at peak load. The critical qualifier is the message payload size – 10,000 msg/s of 100-byte messages is a fundamentally different resource profile from 10,000 msg/s of 10KB messages.

Required inputs:

  • Peak messages per second (not average, size for peak)
  • Average message payload size in bytes
  • Persistence mode: persistent (default) or non-persistent

Why persistence mode dominates throughput: persistent messages require the broker to write the message to the KahaDB journal and receive a filesystem sync confirmation before acknowledging to the producer. For non-persistent messages, no fsync occurs, the message is written to memory and acknowledged immediately. The throughput difference is typically 5-20×:

Message ModeTypical ThroughputBottleneck
Non-persistent, async send20,000–50,000+ msg/sCPU, network bandwidth
Non-persistent, sync send10,000–20,000 msg/sNetwork round-trip
Persistent on NVMe SSD5,000–15,000 msg/sDisk sync writes
Persistent on SATA SSD2,000–8,000 msg/sDisk sync writes
Persistent on spinning HDD200–2,000 msg/sDisk seek + sync
Persistent on NFS mount76–500 msg/sNetwork filesystem sync

These figures from documented benchmarks (Red Hat Engineering, JBoss A-MQ performance testing) illustrate why the storage subsystem must be validated before any other sizing decision.

Dimension 2: Queue Depth (Peak In-Flight Messages)

Queue depth represents the maximum number of messages that will be simultaneously held in the broker’s persistent store, waiting for consumption. This is distinct from throughput, a broker handling 10,000 msg/s with consumers keeping up has near-zero queue depth; a broker with a slow consumer may accumulate millions of messages.

Queue depth drives disk capacity: each message persisted to KahaDB occupies journal storage. A 1 million-message backlog of 1KB messages requires approximately 1GB of journal storage plus index overhead.

Queue depth drives JVM heap: messages that are ready for dispatch to consumers are held in memory by the broker’s message cursor. When queue depth exceeds the cursorMemoryHighWaterMark (70% of memoryUsage by default), the broker pages messages from memory to disk, but this itself requires disk I/O and consumes tempUsage.

Dimension 3: Connection Count

Each client connection (producer or consumer) to the broker consumes:

  • A file descriptor from the OS’s file descriptor table
  • Approximately 1-2MB of JVM heap for connection state (session, producers, consumers, transaction state)
  • Thread stack memory (if UseDedicatedTaskRunner=true)

OS file descriptor limit: the default ulimit -n on many Linux systems is 1,024. For a broker with 500 connections and standard OS tools consuming file descriptors, this is insufficient. Set ulimit -n to at least 4× the expected peak connection count.

Connection heap footprint: 1,000 connections × 1.5MB average = 1.5GB of heap consumed by connection state alone, before any messages are in the system.

Dimension 4: Destination Count

Each destination (queue or topic) in Apache ActiveMQ® creates:

  • An in-memory dispatcher and message cursor
  • Memory tracking objects reporting to the broker’s SystemUsage
  • A dedicated thread (if UseDedicatedTaskRunner=true)
  • Advisory topic subscriptions (if advisorySupport=true)

At scale, destination count becomes a significant heap consumer. A broker with 10,000 destinations and UseDedicatedTaskRunner=true creates 10,000 threads, each consuming thread stack memory from the OS’s virtual address space, independent of the JVM heap.

Dimension 5: Persistence Ratio

The ratio of persistent to non-persistent messages in your workload determines the disk I/O profile. For a workload that is 100% persistent, the disk becomes the primary bottleneck. For a workload that is 90% non-persistent, heap and CPU dominate.

Memory Sizing: The Formula

JVM Heap Sizing

The ÷ 0.70 accounts for memoryUsage percentOfJvmHeap=”70″, the broker uses 70% of the JVM heap for message buffering; the remaining 30% is JVM overhead (Metaspace, GC, thread stacks). This means the heap must be sized so that 70% of it accommodates the peak in-flight message volume.

Example calculation:

  • Peak in-flight: 500,000 messages
  • Average size: 2KB (2,048 bytes)
  • Peak in-memory footprint: 500,000 × 2,048 = 1,024MB = 1GB
  • With 1.5× safety factor: 1.5GB
  • With ÷ 0.70: 2.14GB → round to 4GB (-Xmx4g)

KahaDB indexCacheSize footprint: with the default 10,000 entries and an average index entry of 20-50KB, the index cache can consume 200MB-500MB of heap. For brokers with many destinations and large persistent message volumes, this cache is a significant heap consumer (covered in our JVM Memory & GC Tuning post as a specific OOM root cause).

Apache Artemis™ Heap Sizing

Apache Artemis™ documentation provides an explicit formula: -Xmx should be at least 5× global-max-size. If global-max-size=1GB (the Artemis limit for all addresses combined), then -Xmx should be at least 5GB. The multiplier accounts for GC overhead under high load, with many objects being allocated and collected, the heap needs significant headroom above the working set to avoid continuous full GC.

systemUsage Configuration Aligned with Heap

Once the heap is sized, configure memoryUsage in activemq.xml to match:

We covered the interactions between JVM heap, memoryUsage, and producer flow control in our Producer Flow Control Troubleshooting post. The key connection: memoryUsage is the capacity trigger for PFC. Sizing the heap correctly ensures PFC triggers at the right threshold and not prematurely due to an undersized memoryUsage limit.

Disk Sizing: IOPS is the Critical Variable

Why Sync Write Speed Determines Persistent Throughput

KahaDB with enableJournalDiskSyncs=true (the default) issues an fsync() call after writing each journal record batch. The broker cannot acknowledge a persistent message to the producer until the fsync completes. This means persistent message throughput is fundamentally limited by the number of fsync operations per second the storage system can perform.

A JBoss A-MQ performance case study documented this precisely: on a GFS2 filesystem over a SAN, the sync write speed was 9.7 MB/sec, producing only 76 transactions per second. After switching to preallocation with zeros and optimizing the filesystem, performance improved to approximately 2,000 TPS on the same hardware – a 25× improvement without changing hardware.

Measuring Storage Capacity: ActiveMQ DiskBenchmark

Apache ActiveMQ® ships with a built-in disk benchmark tool that measures the actual sync write throughput of your storage system from Java (which uses the same Java NIO APIs as KahaDB):

Interpreting results:

Sync Writes/secStorage TypePersistent Throughput Ceiling
< 100Spinning HDD, NFS, network SAN< 100 msg/s – unacceptable for production
100–1,000SATA SSD, good SAN50–500 msg/s – acceptable for low-volume workloads
1,000–5,000NVMe SSD500–2,500 msg/s – typical production range
5,000–20,000Local NVMe, enterprise SSD2,500–10,000 msg/s – high-throughput range
> 20,000Ramdisk, enterprise NVMe> 10,000 msg/s – very high throughput

Disk Capacity Sizing Formula

Disk required = (peak_queue_depth × avg_message_size × 1.5)

              + (journal_overhead)
              + (backup_storage if on same volume)

Journal overhead = journalMaxFileLength (32MB default) × active_journal_count
                 ≈ 32MB × 10 = 320MB minimum for active journals

Practical rule: provision at least 3× the daily message volume in bytes as disk capacity, with a minimum of 100GB for a production broker. Set storeUsage limit to 80% of available disk, leaving 20% for journal overhead and operational margin.

Disk type recommendation:

  • Persistent messaging production: NVMe or enterprise SSD — spinning disk is a bottleneck, not a cost optimization
  • Non-persistent messaging or KahaDB on Kubernetes: SSD StorageClass with ReadWriteOnce (see our Kubernetes Deployment post for StorageClass selection)
  • Never: NFS-backed storage for KahaDB with enableJournalDiskSyncs=true, the network filesystem latency for fsync is prohibitive

Getting the Sizing Right Before It Becomes an Incident

Capacity planning errors don’t announce themselves, they manifest as PFC events, OOM crashes, and connection rejections under peak load. meshIQ’s team reviews ActiveMQ deployments and can assess whether your current broker is sized for your current load, your projected growth, and your SLA requirements.

Request a Capacity Review

Connection and Destination Scaling

Connection Limits Configuration

OS-level file descriptor configuration:

# /etc/security/limits.conf (Linux) — set before broker startup
activemq soft nofile 65536
activemq hard nofile 65536

# Verify the limit is applied to the broker process
cat /proc/$(pgrep -f activemq)/limits | grep “open files”

Connection memory budget:

Target Connection CountJVM Heap for ConnectionsMinimum -Xmx
100 connections150-200MB2GB
500 connections750MB-1GB4GB
1,000 connections1.5-2GB6GB
2,000 connections3-4GB8GB
5,000 connections7-10GB16GB

These estimates assume 1.5MB average connection state per connection. Use NIO transport for high connection counts, NIO uses a shared thread pool rather than one thread per connection, dramatically reducing thread stack memory overhead.

Destination Count Scaling

Destination count scaling is less commonly planned for, but becomes critical at enterprise scale. With UseDedicatedTaskRunner=true (Apache ActiveMQ® default), every destination holds a dedicated thread for message dispatch. At 10,000 destinations:

  • 10,000 threads × ~512KB default stack = ~5GB of native memory for thread stacks alone
  • Additionally: 10,000 advisory topics (if advisorySupport=true), each with its own subscription overhead

Recommendations for high destination count brokers:

# In ACTIVEMQ_OPTS — switch from per-destination threads to thread pool
-Dorg.apache.activemq.UseDedicatedTaskRunner=false

<!– activemq.xml — disable advisory support for very high destination counts –>
<!– Advisory messages are required for dynamic NoB, only disable on standalone brokers –>
<broker advisorySupport=”false” …>

With UseDedicatedTaskRunner=false, Apache ActiveMQ® uses a shared thread pool for all destination dispatching. The performance impact is minimal for most workloads, dispatch is already largely asynchronous. The memory impact is significant: from O(destinations) threads to O(1) thread pool.

Destination count reference:

Destination CountConfiguration Required
< 100Default configuration adequate
100–1,000Monitor heap; consider UseDedicatedTaskRunner=false
1,000–10,000UseDedicatedTaskRunner=false required; monitor thread count
> 10,000Consider mKahaDB sharding; advisorySupport=false for standalone; monitor OS thread limits

Network Bandwidth Sizing

Network bandwidth is rarely the primary bottleneck for ActiveMQ, but it can become so for high-volume, large-message workloads. The formula is straightforward:

Required bandwidth (Mbps) = (peak_msg_per_sec × avg_message_size_bytes × 8)
                          × protocol_overhead_factor
                          ÷ 1,000,000

OpenWire protocol overhead factor: ~1.05 (5% overhead — binary, efficient)
AMQP protocol overhead factor: ~1.10 (10% overhead — AMQP framing)
STOMP protocol overhead factor: ~1.20 (20% overhead — text encoding)

Example: 5,000 msg/s × 2KB messages × 8 bits × 1.05 OpenWire factor = 84 Mbps. A standard 1Gbps network interface provides ~12× headroom.

Where bandwidth becomes a concern: large messages (>64KB) at high throughput, or a Network of Brokers topology where bridge connections carry the full combined message volume of all connected brokers.

Capacity Trigger Thresholds

Capacity planning is not a one-time event, it is an ongoing process of monitoring capacity signals and acting before thresholds are crossed. The following thresholds, configured as alerts in your monitoring system (see our Monitoring & Alerting Setup post), define when capacity expansion is needed:

MetricWarning ThresholdCritical ThresholdMeaning
MemoryPercentUsage> 70%> 85%Approaching PFC trigger (100%)
StorePercentUsage> 70%> 85%Approaching disk-PFC trigger (100%)
TempPercentUsage> 70%> 85%Non-persistent spooling pressure
TotalConnectionsCount> 80% of maximumConnections> 90%Connection capacity approaching
AverageEnqueueTime trendIncreasing 7-day trend> 2× baselineThroughput capacity is being saturated
QueueSize sustained growthGrowing for 24 hoursGrowing for 72 hoursConsumer capacity insufficient

Warning → Critical response: When a metric crosses the Warning threshold, the action is to plan expansion and schedule it within the next maintenance window. When a Critical threshold is crossed, the response must be immediate. A critical threshold means you are one load spike away from a service-affecting event.

Vertical vs. Horizontal Scaling Decision Framework

Vertical Scaling: More Resources on the Same Broker

Add CPU, RAM, faster disk, or network bandwidth to the existing broker.

When to choose vertical scaling:

  • The bottleneck is a single resource (disk IOPS, heap, connections) that can be addressed by a hardware upgrade
  • Horizontal scaling would require a significant configuration change (Network of Brokers setup, client-side connection distribution logic)
  • The workload is not yet at a scale where a single broker creates a reliability risk

Vertical scaling limits:

  • JVM heap: practical ceiling is ~30-32GB for G1GC (beyond this, GC pause times grow significantly). Consider ZGC for heaps > 32GB.
  • Disk: NVMe SSDs deliver ~3-4GB/s sequential write and ~500,000 IOPS, this is rarely the ceiling
  • Connections: NIO transport scales to ~5,000-10,000 connections on a 16GB heap broker

Horizontal Scaling: Multiple Brokers

Add additional broker nodes via Network of Brokers (Apache ActiveMQ®) or Apache Artemis™ clustering.

When to choose horizontal scaling:

  • Message throughput exceeds what a single broker can handle on available hardware
  • Connection count exceeds what a single broker can accommodate on the available heap
  • Business continuity requires geographic distribution of message processing
  • Different queues/topics have very different throughput/latency characteristics that benefit from isolation

Horizontal scaling options:

  • Apache ActiveMQ® Network of Brokers: store-and-forward mesh; good for geographic distribution and load isolation
  • Apache Artemis™ clustering: symmetric cluster with shared store or replication; good for HA with load distribution
  • mKahaDB destination sharding: distribute high-volume destinations across separate KahaDB instances on the same broker, a middle ground between vertical and horizontal

We covered Network of Brokers configuration and topology patterns in our Network of Brokers Configuration post, and HA/clustering architecture in our High Availability Architecture Guide .

Continuous Capacity Visibility Across Your ActiveMQ Fleet

meshIQ Console tracks MemoryPercentUsage, StorePercentUsage, AverageEnqueueTime, connection counts, and queue depth trends across all brokers, providing the continuous capacity intelligence that turns capacity planning from a periodic event into an ongoing operational posture.

See It in Action

Capacity Planning Worksheet

Use this worksheet to estimate broker sizing for a new deployment or to validate existing sizing:

Step 1: Characterize the workload

VariableYour ValueNotes
Peak messages/second_____Use peak, not average
Average message size (bytes)_____Include headers (~500B overhead)
Persistent percentage (%)_____JMS default is 100%
Maximum queue depth (messages)_____Max backlog during consumer lag
Peak connection count_____Producers + consumers
Destination count_____Active queues + topics

Step 2: Calculate resource requirements

ResourceFormulaExample (5,000 msg/s, 2KB, 50% persistent)
Required disk sync writes/secpeak_persistent_msg/s2,500 writes/sec
Required JVM heappeak_queue_depth × avg_size × 1.5 ÷ 0.70500K × 2KB × 1.5 ÷ 0.70 = ~4GB
Connection heappeak_connections × 1.5MB1,000 × 1.5MB = 1.5GB
Total JVM heapheap + connections + 30% overhead4GB + 1.5GB × 1.3 = ~7GB → 8GB
Disk capacitymax_queue_depth × avg_size × 3500K × 2KB × 3 = 3GB minimum
Network bandwidthpeak_msg/s × avg_size × 8 × 1.05 ÷ 1,000,0005,000 × 2048 × 8 × 1.05 ÷ 1M = 86 Mbps

Step 3: Validate storage

Run DiskBenchmark and confirm sync writes/sec ≥ 2× peak persistent messages/second.

Step 4: Size the system

ResourceCalculated NeedRecommended Provision (with headroom)
JVM heap8GB8GB (50% of host RAM on 16GB host)
Disk sync writes/sec2,500SSD delivering 5,000+ sync writes/sec
Disk capacity3GB minimum200GB (operational headroom + backups)
Network86 Mbps1Gbps (sufficient)
maximumConnections1,0002,000 (with growth headroom)

Size for Peak, Monitor for the Trigger

Capacity planning errors fall into two categories: sizing for average load (which produces incidents at peak) and sizing without monitoring (which produces surprises). The framework in this guide addresses both: size for peak load using the formulas, then configure capacity trigger thresholds in your monitoring system to alert before the next peak exhausts the headroom you built in.

The DiskBenchmark tool is the step that most organizations skip and most regret. A broker that looks correctly sized on paper can be limited to 76 messages/second by a filesystem that cannot deliver adequate sync write throughput. Measure the storage first — everything else follows from that constraint.

Get your ActiveMQ deployment’s capacity profile reviewed by our team → Request a Capacity Review

Frequently Asked Questions

Q: How many messages per second can ActiveMQ handle? 

Non-persistent: 20,000–50,000+ msg/s. Persistent on NVMe SSD: 5,000–15,000 msg/s. Persistent on SATA SSD: 2,000–8,000 msg/s. Persistent on spinning disk or NFS: 76–2,000 msg/s. The bottleneck is always the disk sync write speed for persistent messages. Use DiskBenchmark to measure your actual storage before sizing.

Q: How much memory does ActiveMQ need? 

Formula: -Xmx ≥ (peak in-flight messages × avg size × 1.5) ÷ 0.70 + connection heap (connections × 1.5MB). For Artemis: -Xmx ≥ 5× global-max-size. Start with 2–4GB for typical workloads, 8GB+ for high-volume or high-connection deployments.

Q: How much disk space does ActiveMQ need? 

Formula: max_queue_depth × avg_message_size × 3 for operational storage, plus KahaDB journal overhead (~320MB minimum). Provision at least 3× daily message volume as disk capacity with a minimum of 100GB. Set storeUsage limit to 80% of available disk.

Q: What are the connection limits for ActiveMQ? 

No hard system limit, practical ceiling is file descriptors (ulimit -n, default 1024 — increase to 4× expected connections) and JVM heap (1–2MB per connection). Use NIO transport for >500 connections. Set maximumConnections on each transport connector to prevent unbounded growth.

Q: How do I know when ActiveMQ needs more capacity?

Monitor five capacity triggers: MemoryPercentUsage > 70% (warning), StorePercentUsage > 70%, connection count > 80% of maximumConnections, AverageEnqueueTime trending upward, and QueueSize sustained growth. Any metric crossing the warning threshold means expansion should be planned for the next maintenance window.

Cookies preferences

Others

Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.

Necessary

Necessary
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.

Advertisement

Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.

Analytics

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.

Functional

Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.

Performance

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.