Connecting Apache ActiveMQ to Prometheus requires different tooling depending on which broker you run. Apache ActiveMQ Classic (latest stable 6.2.5) has no native Prometheus endpoint.
Apache Artemis (latest 2.53.0, now an independent Apache Top-Level Project at artemis.apache.org) requires a separate plugin that must be installed alongside the broker. Neither broker exposes Prometheus metrics out of the box. This guide covers the complete setup for both.
Key Takeaways
- Apache ActiveMQ Classic requires the JMX Prometheus Exporter agent (v1.3.0, May 2025), a JAR that runs inside the broker JVM and converts JMX MBeans to Prometheus metrics at a configurable port
- Apache Artemis does not ship a Prometheus plugin in its standard distribution. You must download artemis-prometheus-metrics-plugin from github.com/rh-messaging/artemis-prometheus-metrics-plugin, copy the JAR to lib/ and metrics.war to web/, and configure both broker.xml and bootstrap.xml
- For Classic, a YAML mapping file controls which JMX MBeans become Prometheus metrics. Without explicit rules, the output is unusable raw JMX data
- Artemis JVM memory metrics are enabled by default. JVM GC and thread metrics are disabled by default and must be explicitly enabled in broker.xml
- Use deriv() on gauge metrics (QueueSize, memory percentages) in alert expressions. Use rate() on counter metrics (EnqueueCount, DequeueCount). Using rate() on a gauge produces unreliable results
- Scrape interval for Classic should be 30 seconds on deployments with many destinations. JMX attribute resolution is synchronous and adds load at each scrape
How Each Broker Exposes Metrics to Prometheus
| Apache ActiveMQ (Classic) | Apache Artemis | |
| What you install | jmx_prometheus_javaagent-1.3.0.jar + YAML mapping file | artemis-prometheus-metrics-plugin.jar (to lib/) + metrics.war (to web/) |
| Configuration file | YAML rules file, loaded at startup via -javaagent flag | broker.xml plugin block + bootstrap.xml web app entry |
| Metrics endpoint | Your chosen port (e.g., 9404) at /metrics | Port 8161 at /metrics |
| Metric naming | Controlled by your YAML rules | artemis_ prefix, Micrometer-managed |
| JVM metrics | Captured via JVM MBeans in the JMX exporter | Memory: on by default. GC and threads: must be enabled |
Step 1A: Install the JMX Exporter for Apache ActiveMQ Classic
Download the agent JAR
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/1.3.0/jmx_prometheus_javaagent-1.3.0.jar \
-O /opt/activemq/jmx_prometheus_javaagent-1.3.0.jarCreate the YAML mapping file
The mapping file tells the exporter which JMX MBean attributes to collect and what Prometheus metric names to assign. Without it, the exporter outputs everything in a raw, unlabeled format. Save this as /opt/activemq/activemq-prometheus.yml:
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
# Broker-level metrics
- pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+)><>(TotalEnqueueCount|TotalDequeueCount|TotalConnectionsCount|TotalConsumerCount|TotalProducerCount|MemoryPercentUsage|StorePercentUsage|TempPercentUsage|TotalMessageCount)'
name: activemq_broker_$2
labels:
broker: "$1"
attrNameSnakeCase: true
# Per-queue metrics
- pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+), destinationType=Queue, destinationName=([^,]+)><>(QueueSize|ConsumerCount|ProducerCount|EnqueueCount|DequeueCount|ExpiredCount|InFlightCount|AverageEnqueueTime|MemoryPercentUsage)'
name: activemq_queue_$3
labels:
broker: "$1"
destination: "$2"
attrNameSnakeCase: true
# Per-topic metrics
- pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+), destinationType=Topic, destinationName=([^,]+)><>(QueueSize|ConsumerCount|ProducerCount|EnqueueCount|DequeueCount|ExpiredCount|InFlightCount|AverageEnqueueTime|MemoryPercentUsage)'
name: activemq_topic_$3
labels:
broker: "$1"
destination: "$2"
attrNameSnakeCase: true
# JVM heap memory
- pattern: 'java.lang<type=Memory><HeapMemoryUsage>(used|committed|max)'
name: jvm_heap_$1_bytes
attrNameSnakeCase: true
# JVM garbage collection
- pattern: 'java.lang<type=GarbageCollector, name=([^,]+)><>(CollectionCount|CollectionTime)'
name: jvm_gc_$2
labels:
gc: "$1"
attrNameSnakeCase: trueRegister the agent in ActiveMQ’s startup config
Edit <activemq-install>/bin/env and append to ACTIVEMQ_OPTS:
ACTIVEMQ_OPTS="$ACTIVEMQ_OPTS \
-javaagent:/opt/activemq/jmx_prometheus_javaagent-1.3.0.jar=9404:/opt/activemq/activemq-prometheus.yml"Restart ActiveMQ:
./bin/activemq restartVerify metrics are being served:
curl -s http://localhost:9404/metrics | grep activemq_brokerExpected output:
activemq_broker_memory_percent_usage{broker=”localhost”} 12.0
activemq_broker_store_percent_usage{broker=”localhost”} 4.0
activemq_broker_total_consumer_count{broker=”localhost”} 3.0
If the endpoint responds but returns no activemq_ metrics, the MBean patterns in your YAML do not match the actual object names on your broker. Open the web console at http://localhost:8161, navigate to the MBeans tab, and verify exact object name formats before adjusting your patterns.
Docker Compose for Classic
services:
activemq:
image: apache/activemq-classic:latest
ports:
- "61616:61616"
- "8161:8161"
- "9404:9404"
volumes:
- ./jmx_prometheus_javaagent-1.3.0.jar:/opt/apache-activemq/bin/jmx_prometheus_javaagent-1.3.0.jar
- ./activemq-prometheus.yml:/opt/apache-activemq/conf/activemq-prometheus.yml
environment:
ACTIVEMQ_OPTS: "-javaagent:/opt/apache-activemq/bin/jmx_prometheus_javaagent-1.3.0.jar=9404:/opt/apache-activemq/conf/activemq-prometheus.yml"Step 1B: Install the Prometheus Plugin for Apache Artemis
The Prometheus plugin is not included in the standard Artemis distribution. The setup involves three files across two configuration locations.
Download the plugin files
curl -L -o artemis-prometheus-metrics-plugin-3.1.0.jar \
https://github.com/rh-messaging/artemis-prometheus-metrics-plugin/releases/download/v3.1.0/artemis-prometheus-metrics-plugin-3.1.0.jar
curl -L -o metrics.war \
https://github.com/rh-messaging/artemis-prometheus-metrics-plugin/releases/download/v3.1.0/metrics.warCopy both files to the broker instance. Create web/ if it does not exist:
cp artemis-prometheus-metrics-plugin-3.1.0.jar <ARTEMIS_INSTANCE>/lib/
mkdir -p <ARTEMIS_INSTANCE>/web
cp metrics.war <ARTEMIS_INSTANCE>/web/Edit broker.xml
Add the <metrics> block as a direct child of <core> in <artemis-instance>/etc/broker.xml. GC and thread metrics are off by default – enable them explicitly:
<metrics>
<jvm-memory>true</jvm-memory>
<jvm-gc>true</jvm-gc>
<jvm-threads>true</jvm-threads>
<plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.ArtemisPrometheusMetricsPlugin"/>
</metrics>Edit bootstrap.xml
Register the metrics web app inside the <web> element in <artemis-instance>/etc/bootstrap.xml:
<web path="web">
<binding uri="http://0.0.0.0:8161">
<app url="activemq-branding" war="activemq-branding.war"/>
<app url="artemis-plugin" war="artemis-plugin.war"/>
<app url="console" war="console.war"/>
<app url="metrics" war="metrics.war"/>
</binding>
</web>Restart the broker:
./bin/artemis-service restartVerify metrics are being served:
curl -s http://localhost:8161/metrics | grep artemis_message_countExpected output:
artemis_message_count{address=”orders”,broker=”0.0.0.0″,queue=”orders”} 0.0
artemis_consumer_count{address=”orders”,broker=”0.0.0.0″,queue=”orders”} 1.0
Step 2: Configure Prometheus to Scrape
Classic (port 9404)
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "activemq-alerts.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: activemq-classic
scrape_interval: 30s
scrape_timeout: 25s
static_configs:
- targets:
- activemq-host1:9404
- activemq-host2:9404
labels:
env: productionArtemis (port 8161)
scrape_configs:
- job_name: activemq-artemis
scrape_interval: 15s
metrics_path: /metrics
static_configs:
- targets:
- artemis-host1:8161
- artemis-host2:8161
labels:
env: productionKubernetes with Prometheus Operator
For Classic, use a PodMonitor targeting the sidecar container port:
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: activemq-classic
namespace: monitoring
spec:
selector:
matchLabels:
app: activemq
podMetricsEndpoints:
- port: jmx-metrics
interval: 30s
scrapeTimeout: 25sFor Artemis, use a ServiceMonitor targeting port 8161:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: activemq-artemis
namespace: monitoring
spec:
selector:
matchLabels:
app: artemis
endpoints:
- port: web
interval: 15s
path: /metricsStep 3: Verify and Query Metrics
After Prometheus starts scraping, confirm data is present at http://prometheus:9090 before writing alert rules.
For Classic:
# Check broker is visible
activemq_broker_memory_percent_usage
# Confirm per-queue metrics have destination labels
activemq_queue_queue_size
# Enqueue rate
rate(activemq_queue_enqueue_count[5m])For Artemis:
# Confirm queue metrics with address and queue labels
artemis_message_count
# Broker disk store usage
artemis_disk_store_usage
# Address memory usage percentage
artemis_address_memory_usage_percentage
# Message add rate
rate(artemis_messages_added_total[5m])If targets show UP but metrics are missing, check the YAML patterns (Classic) or confirm the plugin JAR is in lib/ and bootstrap.xml was saved correctly (Artemis).
Step 4: Alert Rules
groups:
- name: activemq-classic
rules:
- alert: ActiveMQQueueDepthGrowing
expr: deriv(activemq_queue_queue_size[10m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "ActiveMQ queue depth is growing"
description: >
Queue {{ $labels.destination }} on {{ $labels.broker }}
has been growing for 10 consecutive minutes.
- alert: ActiveMQQueueNoConsumers
expr: activemq_queue_consumer_count == 0
for: 2m
labels:
severity: critical
annotations:
summary: "ActiveMQ queue has no consumers"
description: >
Queue {{ $labels.destination }} on {{ $labels.broker }}
has zero consumers.
- alert: ActiveMQMemoryHigh
expr: activemq_broker_memory_percent_usage > 70
for: 5m
labels:
severity: warning
annotations:
summary: "ActiveMQ broker memory above 70%"
description: >
Broker {{ $labels.broker }} memory is at {{ $value }}%.
Producer Flow Control activates at 100%.
- alert: ActiveMQStoreHigh
expr: activemq_broker_store_percent_usage > 70
for: 5m
labels:
severity: warning
annotations:
summary: "ActiveMQ persistent store above 70%"
description: >
Broker {{ $labels.broker }} store is at {{ $value }}%.
- alert: ActiveMQJVMHeapHigh
expr: jvm_heap_used_bytes / jvm_heap_max_bytes * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "ActiveMQ JVM heap above 80%"
description: >
Instance {{ $labels.instance }} heap is at {{ $value | humanize }}%.
- name: activemq-artemis
rules:
- alert: ArtemisQueueDepthGrowing
expr: deriv(artemis_message_count[10m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "Artemis queue depth is growing"
description: >
Queue {{ $labels.queue }} on address {{ $labels.address }}
has been growing for 10 consecutive minutes.
- alert: ArtemisQueueNoConsumers
expr: artemis_consumer_count == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Artemis queue has no consumers"
description: >
Queue {{ $labels.queue }} on address {{ $labels.address }}
has zero consumers.
- alert: ArtemisAddressMemoryHigh
expr: artemis_address_memory_usage_percentage > 70
for: 5m
labels:
severity: warning
annotations:
summary: "Artemis address memory above 70%"
description: >
Broker {{ $labels.broker }} address memory is at {{ $value }}%.
- alert: ArtemisUnroutedMessages
expr: deriv(artemis_unrouted_message_count{address=~".+"}[5m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Artemis has unrouted messages"
description: >
Address {{ $labels.address }} on broker {{ $labels.broker }}
is receiving messages but has no queues bound to it.Common Setup Problems
| Problem | Likely cause | Fix |
| Open http://localhost:8161, browse MBeans, and compare object name formats against your patterns | Verify the path in bin/env, restart, and check the startup log for the javaagent | Classic: endpoint returns nothingThe |
| Verify the path in bin/env, restart, and check startup log for the javaagent | YAML patterns do not match actual MBean object names | Agent JAR path in ACTIVEMQ_OPTS is wrong, or ActiveMQ was not restarted |
| Classic: Prometheus scrape timeouts | Classic: endpoint responds, but no activemq_ metrics | Increase scrape_timeout to 25s and scrape_interval to 30s. Narrow YAML patterns to only needed attributes |
| Artemis: metrics endpoint 404 | metrics.war missing from bootstrap.xml or web/ directory | Add <app url=”metrics” war=”metrics.war”/> to bootstrap.xml and confirm metrics.war exists in web/ |
| Artemis: no metrics after broker.xml change | <metrics> block outside <core>, or plugin JAR missing from lib/ | Confirm <metrics> is a direct child of <core>. Confirm the JAR is in the broker’s lib/ directory |
| Artemis: GC and thread metrics absent | Disabled by default | Add <jvm-gc>true</jvm-gc> and <jvm-threads>true</jvm-threads> inside the <metrics> block |
| Both: Prometheus shows target DOWN | Firewall blocking scrape port | Open port 9404 (Classic) or 8161 (Artemis) from the Prometheus host |
Connecting Broker Metrics to Application Traces
A working Prometheus setup gives you visibility into the broker. It tells you the queue depth is climbing, memory is under pressure, or a consumer has disappeared. What it does not give you is a path from the broker symptom to the root cause in your application.
When an alert fires and you check the dashboard, the next question is invariably: is this a throughput problem (not enough consumer instances), a processing problem (each message takes too long), or a dependency problem (the consumer is waiting on a slow downstream call)? Those three causes look identical on a queue depth chart.
CubeAPM instruments your consumer application via OpenTelemetry and correlates application traces directly with broker metrics. When ActiveMQQueueDepthGrowing fires, you can jump from the Prometheus alert into CubeAPM and see exactly which consumer instance is falling behind, the per-message processing time broken down by span, and which downstream service call inside the consumer is responsible. The setup works the same whether your broker is ActiveMQ Classic, Artemis, RabbitMQ, or Kafka. CubeAPM runs self-hosted inside your own infrastructure at $0.15/GB ingestion pricing.
Summary
The difference between monitoring ActiveMQ Classic and Artemis with Prometheus is entirely in the installation step. Classic requires the JMX Prometheus Exporter agent and a YAML mapping file. Artemis requires the separately downloaded artemis-prometheus-metrics-plugin JAR and metrics.war, plus two config file edits. Once metrics are flowing, Prometheus scrapes both on the same cadence, and the same alert rule patterns apply to both.
| Your chosen port (e.g., 9404) at /metrics | Classic | Artemis |
| Install | Add javaagent to ACTIVEMQ_OPTS in bin/env | Copy JAR to lib/, WAR to web/, edit broker.xml + bootstrap.xml |
| Metrics port | Your chosen port (e.g. 9404) at /metrics | Port 8161 at /metrics |
| Scrape interval | 30 seconds | 15 seconds |
| Verify | curl localhost:9404/metrics | curl localhost:8161/metrics |
| Queue depth metric | activemq_queue_queue_size | artemis_message_count |
| Consumer count metric | activemq_queue_consumer_count | artemis_consumer_count |
| Broker memory metric | activemq_broker_memory_percent_usage | artemis_address_memory_usage_percentage |
| Alert for gauges | deriv() | deriv() |
| Alert for counters | rate() | rate() |
Disclaimer: Configuration syntax, metric names, and plugin details are verified against Apache ActiveMQ Classic 6.2.5 documentation (activemq.apache.org), Apache Artemis 2.53.0 documentation (artemis.apache.org/components/artemis/documentation/latest/metrics.html), and the artemis-prometheus-metrics-plugin GitHub repository (github.com/rh-messaging/artemis-prometheus-metrics-plugin) as of May 2026.
Also read:
What Are the Key ActiveMQ Metrics to Monitor for Performance?
RabbitMQ Alerts: How to Alert on RabbitMQ Queue Depth and Consumer Count





