Skip to content

Monitoring Aeron®: Metrics and Alerting for Your Observability Platform

Aeron exposes a rich set of lock-free counters in shared memory — but it ships no /metrics endpoint. To get those signals onto your company’s Prometheus/Grafana/Datadog stack you either write a small exporter (OSS) or run Aeron Insights (Premium). This page covers both paths, then maps the counters that actually matter to the failure modes they predict and the alert conditions to set.

Every Aeron media driver maintains a command-and-control file, cnc.dat, memory-mapped in its Aeron directory. Among its regions are a counters metadata buffer, a counters values buffer, and an error-log buffer. Counters are plain 64-bit values updated lock-free by the driver; any external process can memory-map the file read-only and read them live, with — in Aeron’s own words — “no significant impact on performance.”

That is the whole integration surface. There is no push, no agent protocol, no HTTP — you map the file and iterate. The reference reader is AeronStat (the aeron-stat script), which loops once per second and prints every counter. Under the hood it does exactly what any exporter does:

// The canonical read pattern — Agrona's CountersReader over the mapped cnc.dat
final CountersReader counters = cncFileReader.countersReader();
counters.forEach((counterId, typeId, keyBuffer, label) -> {
final long value = counters.getCounterValue(counterId);
// → emit {counterId, typeId, label, value} to your metrics client
});

Counters are grouped by type id: 0–99 client/driver, 100–199 archive, 200–299 cluster. That type id is your routing key when you turn counters into metrics.

Path 1 — OSS: write (or reuse) a counter exporter

Section titled “Path 1 — OSS: write (or reuse) a counter exporter”

Aeron OSS has no native Prometheus exporter. This is by design, and Aeron’s own cookbook points you at the DIY route: “Read the counter data using CountersReader” and feed a metrics system “such as DataDog or Prometheus.” The pattern is a per-node sidecar that maps that node’s cnc.dat, iterates counters on a timer, and exposes /metrics (Prometheus scrape) or pushes to StatsD/Datadog/CloudWatch.

You don’t have to write it from scratch — a couple of community exporters already implement this:

ExporterWhat it isState
singleplayer88/aeron-exporterReads cnc.dat via CncFileReader/CountersReader, serves /metrics (Jetty). Apache-2.0.Recently maintained (2026)
fairtide/aeron-prometheus-statSame CnC-reader pattern, renames counters to Prometheus conventions; ships Docker Compose. Apache-2.0.Older (~2023)
scalecube/scalecube-metricsTelemetry library over Agrona counters + HdrHistogram, with a Prometheus-over-Aeron demo.Actively maintained

For Datadog, StatsD, or CloudWatch there is no off-the-shelf integration — the same CountersReader.forEach loop pushes to that platform’s client instead of exposing /metrics. A log-scraping fallback (parse the once-per-second aeron-stat console output) works but is strictly worse: coarser, lossy, and it re-parses text you already have as numbers.

Aeron Insights (Premium, currently beta) is the turnkey version of Path 1. It ships Prometheus exporters, a sample Grafana dashboard, and a CLI. The exporters “gather data by reading the files created by Aeron, principally the CnC file” plus the archive/cluster mark files — i.e. the same read-only approach, packaged and supported — in two per-node delivery models:

  • HTTP-server exporter (default :8080, localhost) — Prometheus scrapes each node directly. This is the common approach.
  • Push-client exporter — pushes to a Prometheus Push Gateway on an interval (default 1s).

The payoff is that Insights emits named, documented Prometheus metrics out of the box (cluster_*, driver_*, archive_*), with a Grafana “Cluster Overview” dashboard, instead of raw type-id’d counters you have to name and curate yourself.

OSS DIY exporterPremium Aeron Insights
Readscnc.dat (via your code)cnc.dat + mark files (packaged)
Metric namesYou define themDocumented cluster_* / driver_* / archive_*
Grafana dashboardYou build itSample “Cluster Overview” ships
DeliveryWhatever you writeHTTP scrape or Push Gateway
SupportCommunity / selfAdaptive (beta)
CostFree (Apache-2.0)Subscription

The counters that matter (and what bad looks like)

Section titled “The counters that matter (and what bad looks like)”

You do not need all of Aeron’s counters on a dashboard. For a clustered matching engine, these are the load-bearing ones. OSS names are the counter labels / type ids from the CnC file; the Insights column is the Prometheus metric name where one exists.

SignalOSS counter (type id)Insights metricRead it as
Commit positionCluster commit-pos: (203)cluster_commit_position_totalThe heartbeat of the cluster — quorum-replicated log position
Append positionArchive rec-pos of the log recording(no dedicated metric)Where the leader has appended; gap vs commit = follower lag
Node roleCluster node role (201)cluster_node_role2 = Leader, 0 = Follower, 1 = Candidate
Consensus stateConsensus Module state (200)cluster_consensus_module_state1 = Active, 3 = taking a snapshot
Election state / countCluster election state (207) / election count (238)cluster_election_stateSteady state = 17 (Closed); count should never climb
CM cycle timeCluster max cycle time in ns (216) / exceeded (217)cluster_max_cycle_time_secondsDuty-cycle latency of the consensus module
Service cycle timeCluster container max cycle time in ns (218) / exceeded (219)(service-labelled cycle metric)Duty-cycle latency of your service code
ErrorsCluster Errors (212) / Cluster Container Errors (215)cluster_errors_totalMonotonic tally — any delta warrants a look
Valid snapshotsCluster snapshot count (205)cluster_valid_snapshotsCount of usable snapshots
Snapshot age(derive from recording-log timestamp)cluster_last_snapshot_timestamp_secondsOld snapshot ⇒ long replay on recovery
Snapshot durationTotal max snapshot duration in ns (234) / exceeded (235)cluster_snapshot_duration_threshold_exceeded_totalLong snapshots stall the hot path (OSS)
Back-pressuresnd-bpe (13) / Sender flow control limitsdriver_sender_bpe_total”Should be zero in a healthy system”
Loss / NAKNAKs sent/received, Retransmits sent (type 0)driver_nak_messages_*_total, driver_retransmits_sent_totalNetwork loss ⇒ replication drag
Liveness(freshness of counter updates)cluster_activity_age_seconds, driver_heartbeat_age_secondsSeconds since the component was last seen active

Alerting: mapping metrics to failure modes

Section titled “Alerting: mapping metrics to failure modes”

Each of these ties a metric to a failure-mode runbook scenario. Aeron publishes almost no numeric thresholds — the timers below are documented; the rest are operator judgment, so calibrate against your own baseline and start loose to avoid alert fatigue.

AlertConditionWhy / documented basis
Commit stall (Critical)commit-pos delta == 0 while state == Active for > ~10 sAeron’s own “No catchup progress” warning fires when commit doesn’t advance for longer than the leader heartbeat timeout (default 10 s). Causes: slow app code, machine load, GC.
No leader (Critical)No node reports role == Leader (2) for > election timeoutElection in progress = cluster not committing.
Election churn (High)election count (238) increasing, or election state not returning to 17 (Closed)Any increment is a p99 cliff. Triggers: heartbeat timeout, lost quorum. (“Election storm” is not a documented term — set your own rate.)
Follower lag (Medium)rec-pos(log) − commit-pos growing over timeLeader appending faster than a quorum acks — heading toward back-pressure or quorum loss. No documented gap threshold.
Cycle-time breach (Medium)cycle time exceeded counters (217/219) incrementingDuty-cycle threshold default is 100 ms for CM and service. Spikes = GC / CPU starvation; a 10 ms cycle is a 10 ms hole in p99.
Errors rising (High)Cluster Errors (212) delta > 0Then read the text via ClusterTool errors. No numeric rate documented.
Snapshot too old (Medium)now − cluster_last_snapshot_timestamp_seconds > your recovery budgetSnapshots are manual in Aeron — an old snapshot means a long log replay on restart. See Snapshot Failure and Recovery.
Snapshot too slow (Low/Medium)snapshot-duration exceeded (235) incrementingThreshold default 1 s. In OSS the snapshot stalls the hot path; on the leader that lands on max latency.
Back-pressure / loss (Medium)snd-bpe, NAK, or retransmit rate rising above baselineSubscribers/followers not keeping up, or network loss dragging replication. No documented rate threshold.
Storage exhaustion (Critical)Free space on archive/cluster volumes below your floorArchive storage exhaustion throws ClusterTerminationException and terminates the node — the one snapshot/archive failure that is always terminal. Alert before it happens.
Node dark (Critical)cluster_activity_age_seconds / driver_heartbeat_age_seconds climbing, or scrape target downThe component stopped updating counters — process dead or wedged.

If you build one panel set, make it this — ordered top to bottom the way you’d triage:

  1. Is it up? Node role per node (exactly one Leader), liveness/heartbeat age, scrape targets up.
  2. Is it progressing? Commit-position rate (your real throughput) and the append−commit gap.
  3. Is it stable? Election count (flat line = healthy) and consensus-module state.
  4. Is your code the bottleneck? Service cycle time vs consensus cycle time — if service ≫ consensus, the problem is in your handler, not Raft.
  5. Is the substrate healthy? Back-pressure, NAK/retransmit rate, archive/disk free space.
  6. Can you recover fast? Snapshot age, valid-snapshot count, snapshot-duration breaches.