Skip to content

Alert rules

mikroscope dashboards gen writes, beside each dashboard, a Grafana unified-alerting provisioning file with the rules that follow from the dashboards’ own fault counters, from the kernel log the agent reads, and from the collector’s detections. This page answers what those rules are, what each one fires on and what silence means for it, how to install the file, and where every threshold comes from. Every threshold is zero (a counter that should not move), one sample (the silent-agent rule), or a share of a ceiling the device itself published. None is a number compiled in for one router.

File Rules Query language
dashboards/mikroscope-alerts-influxdb.yaml 10 InfluxDB 3 SQL
dashboards/mikroscope-alerts-prometheus.yaml 11 PromQL

The InfluxDB file has one rule fewer because “The sampler is slipping ticks” has no SQL form: the slipped-tick counter is exposed on the agent’s /metrics and is not written to InfluxDB.

Each file is apiVersion: 1 with one rule group, mikroscope, in a folder named mikroscope, organisation 1, evaluated every minute. The rules are provisioned rather than built into the dashboards, so an operator who wants none copies nothing.

Provisioning files do not resolve a dashboard’s ${DS_MIKROSCOPE} input, so the datasource is a literal placeholder, DS_UID_PLACEHOLDER, that you replace with your datasource’s UID before Grafana reads the file:

Terminal window
sed 's/DS_UID_PLACEHOLDER/<uid>/g' dashboards/mikroscope-alerts-influxdb.yaml \
> /etc/grafana/provisioning/alerting/mikroscope-alerts-influxdb.yaml

The provisioning directory is Grafana’s; /etc/grafana/provisioning/alerting/ is the path the generated file’s own header names. Use the file that matches the datasource the UID belongs to.

Every rule has the same shape, the one Grafana’s own rule editor writes:

  1. A — the query, against your datasource, with a relative time range of the last 600 s. Every SQL query and every Prometheus counter query also bounds its own window (2 minutes, 5 minutes or 1 hour, below); the two Prometheus gauge rules, thermal and conntrack, read the latest value.
  2. B — reduce A to one number per series with last, dropping non-numeric values.
  3. C — compare B against the threshold. C is the rule’s condition.

Each rule carries the labels severity (critical or warning) and source: mikroscope, a summary annotation, and execErrState: Error. What Grafana then does with a rule whose query fails is Grafana’s behaviour, set out in its own documentation, and has not been tested here.

The alert rules
Rule (uid)Fires whenThreshold (C)SeverityforNo data meansStores
mikroscope-agent-silentfewer than 1 new sample reached the store in the last 2 minutes< 1critical2mAlertingInfluxDB only
mikroscope-softnet-dropssoftnet_stat dropped a packet in the last 5 minutes> 0critical0sOKInfluxDB only
mikroscope-oom-kill/proc/vmstat oom_kill moved in the last 5 minutes> 0critical0sOKInfluxDB only
mikroscope-detectionsany detection in the last 5 minutes> 0warning0sOKInfluxDB only
mikroscope-thermal-near-criticala zone at or above 85 % of its own critical trip> 0critical1mOKInfluxDB only
mikroscope-conntrack-near-limitnf_conntrack active objects above 0.8 of the kernel's limit> 0.8warning5mOKInfluxDB only
mikroscope-ticks-slippedthe sampler slipped a tick in the last 5 minutes> 0warning5mOKPrometheus only
mikroscope-agent-oomthe agent's own cgroup recorded an OOM kill in the last 5 minutes> 0critical0sOKInfluxDB only
mikroscope-l2-loopan own-address record on any port in the last 5 minutes> 0critical0sOKboth
mikroscope-port-link-downa link-down record on any port in the last 5 minutes> 0warning0sOKboth
mikroscope-ecc-failurethe NAND reported an uncorrectable ECC failure in the last hour> 0critical0sOKInfluxDB only

The InfluxDB form of mikroscope-conntrack-near-limit is broken; the “what has not been tried” note at the end of this page says why. “No data means” is the rule’s noDataState. The silent-agent rule is the one where silence is the fault, so no data fires it; for every other rule no data is the healthy reading.

Each rule’s title, and under it its summary annotation verbatim, as generated:

  • mikroscope agent stopped delivering samples. “No new samples reached the store in the last two minutes: the agent stopped, the collector stopped, or the path between them did. Every other rule is blind while this one fires.”
  • Packets dropped in the kernel receive path. “softnet_stat dropped a packet: a per-CPU backlog was full. Unambiguous loss inside the router, invisible to every SNMP and RouterOS counter. Zero is the expected reading.”
  • The kernel OOM-killed a process. “/proc/vmstat oom_kill moved: the kernel killed a process to get memory back. Which process is not knowable from the container (no PID namespace).”
  • The collector’s derive stage flagged an event. “A detection rule fired (counter-reset, agent-restart, agent-oom, microburst, reboot, link-flap, conntrack-cliff, conntrack-high, thermal-high, thermal-rising, ipc-collapse). The rule, key, value and threshold are in the Detections section and on the dashboard as an annotation.”
  • A thermal zone is within 15 % of its own critical trip. “The reading is at or above 85 % of the zone’s declared critical trip point (105 C on the reference RB5009). The ceiling is the board’s own, read from /sys, not a number compiled in.”
  • The connection table is above 80 % of nf_conntrack_max. “nf_conntrack active objects over the kernel’s own ceiling. Past the ceiling the router drops new connections. The limit is the sysctl the agent read, not a compiled number.”
  • The sampler is slipping ticks. “Ticks finished after the next was due. The rate is not being delivered: the device is starved, the source set is too expensive for the rate, or the container’s CPU quota throttled the agent (see the observer’s throttling counter).”
  • mikroscope’s own container was OOM-killed. “The kernel killed a process inside the agent’s cgroup: the capture ring and the captures are gone, and every number in the window is suspect. Raise –memory-max or lower RATE_HZ, BUFFER_S or CAPTURE_MB.”
  • The bridge received its own address back: a layer-2 loop signature. “The kernel log reported received packet on <port> with own address as source address: a frame the router sent came back in, which is what a loop through a downstream switch or access point looks like. The port label says which cable. Read from /dev/kmsg by the agent, no API. On the reference RB5009 this ran at 1.49 records/s for hours on 2026-09-12 while every RouterOS counter looked healthy. The InfluxDB form needs a store that has held at least one port record classified by kind.”
  • A port’s link went down. “The kernel log reported a link-down on a port: a cable pulled, a peer rebooted or powered off, a renegotiation. The collector’s link-flap detection covers the repeated case; this is the single event. Read from /dev/kmsg by the agent, no API; the port, its comment and its role are on the Kernel log section’s port events.”
  • The NAND reported an uncorrectable ECC failure. “ecc_failures rose on an MTD partition: a read the error correction could not fix, i.e. data loss on the flash. Any increment is an incident.”
# mikroscope-agent-silent (< 1)
sum(increase(mikroscope_samples_total[2m]))
# mikroscope-softnet-drops (> 0)
sum(increase(mikroscope_softnet_total{kind="dropped"}[5m]))
# mikroscope-oom-kill (> 0)
sum(increase(mikroscope_vm_events_total{event="oom_kill"}[5m]))
# mikroscope-detections (> 0)
sum(increase(mikroscope_collector_detections_total[5m]))
# mikroscope-thermal-near-critical (> 0)
count(mikroscope_thermal_celsius >= on(zone) 0.85 * mikroscope_thermal_critical_celsius)
# mikroscope-conntrack-near-limit (> 0.8)
max(mikroscope_slab_active_objects{cache="nf_conntrack"} / mikroscope_slab_limit_objects{cache="nf_conntrack"})
# mikroscope-ticks-slipped (> 0)
sum(increase(mikroscope_slipped_total[5m]))
# mikroscope-agent-oom (> 0)
sum(increase(mikroscope_self_oom_kills_total[5m]))
# mikroscope-l2-loop (> 0)
sum(increase(mikroscope_kmsg_port_records_total{kind="own-address"}[5m]))
# mikroscope-port-link-down (> 0)
sum(increase(mikroscope_kmsg_port_records_total{kind="link-down"}[5m]))
# mikroscope-ecc-failure (> 0)
sum(increase(mikroscope_mtd_ecc_failures_total[1h]))

mikroscope_slipped_total comes from the agent scrape job, the one with the keep list on Import and check; the other ten read the collector’s /metrics. mikroscope_kmsg_port_records_total is among them: both expositions are written by the same renderer, and the collector’s copy is the one the keep list leaves in place, the one that classifies a record the agent did not and names each port as RouterOS names it now.

  • Zero, for a counter that should not move. Kernel RX drops, kernel OOM kills, detections, slipped ticks, the agent’s own OOM kills, uncorrectable ECC failures, and the kernel-log port records whose kind is own-address or link-down. A healthy device reads zero on each.
  • One sample, for the silent agent. Fewer than one sample in two minutes is none at all, at any configured rate.
  • A share of the board’s own thermal trip. 85 % of the lowest critical trip point each zone declares, which the agent reads from /sys/class/thermal and ships beside every reading. A zone that declares no critical trip is left out of the query rather than compared against a made-up ceiling.
  • A share of the kernel’s own connection limit. 0.8 of the nf_conntrack limit the agent read. The occupancy comes from /proc/slabinfo, which needs a privileged container.

The rules do not alert on softnet squeezes. Measured on the reference RB5009 on 2026-09-15 over 3 476 samples, about 11.2 % of samples carry one squeeze, and alerting on “squeeze > 0” would page forever. What reaches the detections alert instead is the microburst detection: three burst samples on one CPU within 60 s. A burst sample is one in which a softnet queue dropped a packet, or ran out of budget more often than that CPU’s trailing 90th percentile and at least three times, while the sample’s packet count was at or below its trailing median. See Detections.

  • Which process. The OOM rule says the kernel killed something. From inside the container there is no PID namespace to say what.
  • Which port. The two port-event queries sum over ports, so a firing rule says a loop signature or a link-down happened, not on which cable. The port, its comment and its interface lists are on the Kernel log section’s two port-event panels.
  • An unprivileged agent’s blind spots. The connection-table and ECC rules read sources that need a privileged container. Without one those measurements never reach the store, and on Prometheus a query over a missing metric returns no data — which these rules read as OK.
  • Anything while the silent-agent rule fires. Every other rule but the slipped-ticks rule, which reads the agent directly, reads the same stream; with no samples arriving they read zero or no data, and both are OK.