Skip to content

Complete Metrics Reference

This page provides a complete reference of all Prometheus metrics exposed by opnsense2otel. The opnsense_instance label is applied to all metrics.

Summary

  • Total metrics: 1006
  • Gauges: 655
  • Counters: 351

General

ACME Client

Metric NameTypeLabelsDescription
opnsense_exporter_build_infoGaugeversion, goversionBuild information of opnsense2otel (value is always 1; see labels)
opnsense_exporter_collector_enabledGaugecollectorWhether a collector is enabled (1) or disabled (0) in this exporter instance, by subsystem
opnsense_exporter_scrape_collector_duration_secondsGaugecollectorDuration of the latest scheduled sub-collector poll in seconds. The metric name retains its historical scrape_collector prefix for compatibility
opnsense_exporter_scrape_collector_successGaugecollectorWhether the latest scheduled sub-collector poll succeeded (1 = ok, 0 = error or panic). The metric name retains its historical scrape_collector prefix for compatibility
opnsense_exporter_collector_poll_interval_secondsGaugecollectorConfigured poll interval of a collector in seconds (the internal poll scheduler runs each collector on its own interval; #336)
opnsense_exporter_collector_last_poll_timestamp_secondsGaugecollectorUnix timestamp of a collector's last poll ATTEMPT, successful or not; scheduler liveness, not data freshness — a collector failing every poll keeps advancing this while replaying old data. Use collector_snapshot_timestamp_seconds for data age. Absent until the collector has polled at least once (#336, #382)
opnsense_exporter_collector_next_poll_timestamp_secondsGaugecollectorUnix timestamp of a collector's next scheduled poll, read from the scheduler's actual fixed-cadence deadline (not derived from last poll + interval); absent when no poller is running for the collector (#336, #385)
opnsense_exporter_collector_snapshot_timestamp_secondsGaugecollectorUnix timestamp at which a collector's stored metric buffer was last REPLACED — the true age of the data a scrape replays. Advances on a successful poll and on a partial-error poll that still emitted data; does NOT advance when a failed poll emitted nothing and the last-good buffer was retained. Absent until the collector has stored data at least once (#382)
opnsense_exporter_collector_last_success_timestamp_secondsGaugecollectorUnix timestamp of a collector's last fully successful poll. Unlike collector_snapshot_timestamp_seconds this does NOT advance on a partial-error poll, so the two together distinguish 'refreshed but degraded' from 'fully healthy'. Absent until the collector has succeeded at least once (#382)
opnsense_exporter_series_totalGauge---Total number of Prometheus series produced by the COLLECTOR registry on the most recent real /metrics scrape or OTLP export (#494) — the same set --exporter.series-budget is compared against, and what metricsnap replays to the web UI's /cardinality report. Self-metrics on the separate self registry (process_/go_, the opnsense_exporter_otlp_* delivery-health family, and this gauge itself) are NOT included, so this reads lower than a full scrape's total series count; see --exporter.series-budget's flag help for the same caveat. This gauge is itself exactly one series, so it cannot meaningfully move the number it reports. Reads 0 before the first real scrape/export has completed.
opnsense_upGauge---Whether the OPNsense API was reachable on the last health poll (1 = reachable, 0 = unreachable), updated on --collector.poll-interval independently of scrapes. A reachable box reporting a degraded subsystem stays 1; see opnsense_system_status_code and the per-subsystem status metrics.
opnsense_firewall_statusGauge---Status of the firewall reported by the system health check (1 = ok, 0 = errors)
opnsense_crash_reporter_statusGauge---Status of the crash reporter reported by the system health check (1 = ok/no crash reports, 0 = crash reports present)
opnsense_system_status_codeGauge---Numeric OPNsense system status code from the health check (2 = OK, 1 = NOTICE, 0 = WARNING, -1 = ERROR; OPNsense >= 25.1)
opnsense_system_subsystem_status_codeGaugesubsystemNumeric OPNsense SystemStatusCode (2 = OK, 1 = NOTICE, 0 = WARNING, -1 = ERROR) for every health-check subsystem present in the response, by subsystem short name (e.g. diskspace, rootlock, crashreporter, firewall, plus any plugin-contributed key). OPNsense omits healthy subsystems from the report, so a subsystem's series is present only while it is unhealthy; absence should be read as healthy, the same convention as opnsense_firewall_status and opnsense_crash_reporter_status.
opnsense_exporter_scrapes_totalCounter---Total number of times this exporter served a /metrics scrape. Since #336 a scrape replays the in-memory poll snapshot and makes no OPNsense API call, so this counts SERVING, not collection: it tracks how often Prometheus asked, never how often the firewall was polled. For polling use opnsense_exporter_collector_last_poll_timestamp_seconds and opnsense_exporter_api_requests_total.
opnsense_exporter_endpoint_errors_totalCounterendpointTotal number of errors by endpoint returned by the OPNsense API during data fetching. The endpoint label is an api/* path for normal fetch errors; a recovered collector panic uses a 'panic:' sentinel value instead.
opnsense_exporter_api_requests_totalCounterendpoint, codeTotal number of OPNsense API requests made, by endpoint (api/* path) and HTTP response code (0 = no response, e.g. network error or context cancellation). Provides the denominator for a per-endpoint error rate alongside opnsense_exporter_endpoint_errors_total.
opnsense_exporter_api_request_duration_secondsHistogramendpointDuration of individual OPNsense API requests in seconds, by endpoint (api/* path). Lets operators see which underlying endpoint call regressed when a collector's scheduled poll duration spikes.
opnsense_exporter_api_cache_hits_totalCounterendpoint, kindTotal number of OPNsense API calls served from the response cache instead of the firewall, by endpoint (api/* path) and kind. kind=\"body\" is a replayed payload from a slow-moving endpoint (--exporter.cache-ttl / --exporter.firmware-cache-ttl); kind=\"absent\" is a replayed 404 from a plugin-gated endpoint, meaning the plugin is not installed. Only endpoints with a configured TTL are counted, so this and opnsense_exporter_api_cache_misses_total form a hit rate for the cache itself.
opnsense_exporter_api_cache_misses_totalCounterendpointTotal number of OPNsense API calls that went to the firewall and populated the response cache - a cold cache or an expired TTL. This is the denominator for a cache hit rate alongside opnsense_exporter_api_cache_hits_total. A call whose response was never cacheable is NOT counted: notably a 200 from a plugin-gated endpoint whose plugin IS installed, whose live payload is fetched on every scheduled poll by design (only its 404 would be cached).
Metric NameTypeLabelsDescriptionDisable Flag
opnsense_acme_certificates_totalGauge---Total number of ACME-managed certificates--exporter.disable-acme
opnsense_acme_certificate_last_update_timestamp_secondsGaugename, descriptionUnix timestamp of the last successful ACME certificate issue or renewal (0 = never)--exporter.disable-acme
opnsense_acme_certificate_status_codeGaugename, descriptionNumeric ACME operation status code from the last ACME run (100 = default/unknown)--exporter.disable-acme
opnsense_acme_certificate_status_last_update_timestamp_secondsGaugename, descriptionUnix timestamp of the last ACME client run for this certificate (0 = never run)--exporter.disable-acme
opnsense_acme_certificate_enabledGaugename, descriptionWhether this ACME-managed certificate is enabled (1 = enabled, 0 = disabled)--exporter.disable-acme
opnsense_acme_certificate_infoGaugename, description, alt_namesACME certificate information (value is always 1; use labels)--exporter.disable-acme

APC UPS (apcupsd)

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_apcupsd_service_runningGauge---Whether the apcupsd service is running (1 = running, 0 = stopped/disabled)--exporter.disable-apcupsd
opnsense_apcupsd_ups_infoGaugemodel, statusUPS information (value is always 1; see model and status labels)--exporter.disable-apcupsd
opnsense_apcupsd_battery_charge_percentGauge---UPS battery charge percentage (BCHARGE)--exporter.disable-apcupsd
opnsense_apcupsd_battery_runtime_secondsGauge---Estimated UPS battery runtime in seconds (TIMELEFT minutes × 60)--exporter.disable-apcupsd
opnsense_apcupsd_battery_voltage_voltsGauge---UPS battery voltage in volts (BATTV)--exporter.disable-apcupsd
opnsense_apcupsd_input_voltage_voltsGauge---UPS input line voltage in volts (LINEV)--exporter.disable-apcupsd
opnsense_apcupsd_output_voltage_voltsGauge---UPS output voltage in volts (OUTPUTV)--exporter.disable-apcupsd
opnsense_apcupsd_load_percentGauge---UPS load percentage (LOADPCT)--exporter.disable-apcupsd
opnsense_apcupsd_time_on_battery_secondsGauge---Elapsed time on battery power in seconds (TONBATT)--exporter.disable-apcupsd
opnsense_apcupsd_transfers_totalCounter---Cumulative number of transfers to battery (NUMXFERS)--exporter.disable-apcupsd
opnsense_apcupsd_nominal_power_wattsGauge---UPS nominal output power in watts (NOMPOWER)--exporter.disable-apcupsd
opnsense_apcupsd_status_onlineGauge---1 when UPS status contains ONLINE--exporter.disable-apcupsd
opnsense_apcupsd_status_on_batteryGauge---1 when UPS status contains ONBATT--exporter.disable-apcupsd
opnsense_apcupsd_status_low_batteryGauge---1 when UPS status contains LOWBATT--exporter.disable-apcupsd

ARP Table

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_arp_table_entries_totalGauge---Total number of ARP table entries (low-cardinality aggregate, always emitted)--exporter.disable-arp-table
opnsense_arp_table_entriesGaugeip, mac, hostname, interface_description, type, expired, permanent, manufacturer, deviceOne series per ARP table entry (value is always 1). Only emitted when --exporter.enable-arp-details is set (high, churning cardinality). manufacturer is the OUI lookup for the MAC and is the label that actually identifies a device: hostname is empty on every entry on the reference box and is frequently empty upstream in general, so do not build a panel that depends on it. device is the raw kernel device and interface_description the assigned name; they diverge on VLAN children and bridges, and only device joins against the interfaces metrics.--exporter.disable-arp-table

Activity

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_activity_threads_totalGauge---Total number of threads on the system--exporter.disable-activity
opnsense_activity_threads_runningGauge---Number of running threads on the system--exporter.disable-activity
opnsense_activity_threads_sleepingGauge---Number of sleeping threads on the system--exporter.disable-activity
opnsense_activity_threads_waitingGauge---Number of waiting threads on the system--exporter.disable-activity
opnsense_activity_arc_component_bytesGaugecomponentZFS ARC size by component (MFU, MRU, anonymous, header, other), from top's ARC header. MFU versus MRU is the standard read on whether the cache is serving a working set or thrashing. Absent entirely on a non-ZFS install. The ARC total is opnsense_system_memory_arc_bytes.--exporter.disable-activity
opnsense_activity_arc_compressed_bytesGauge---Size of ZFS ARC contents as held in memory, compressed. Divide the uncompressed figure by this to get the compression ratio at query time.--exporter.disable-activity
opnsense_activity_arc_uncompressed_bytesGauge---Logical size of ZFS ARC contents before compression. Absent when top reports no compression line.--exporter.disable-activity
opnsense_activity_user_cpu_percentGaugeuserWeighted CPU percentage summed across every thread owned by this username, from top's WCPU column. Sums per thread, so a busy multi-threaded process can exceed 100.--exporter.disable-activity
opnsense_activity_user_memory_bytesGaugeuserResident memory summed across the processes owned by this username, from top's RES column. Counted once per PROCESS: top prints one row per thread and every thread repeats its process's RES, so the rows are deduplicated by PID before summing.--exporter.disable-activity
opnsense_activity_command_cpu_percentGaugecommandWeighted CPU percentage summed across every thread of this command, from top's WCPU column. The command name is normalised: the {thread-name} suffix and [] kernel brackets are stripped.--exporter.disable-activity
opnsense_activity_command_memory_bytesGaugecommandResident memory summed across the processes running this command, from top's RES column. Counted once per PROCESS, deduplicated by PID — every thread row repeats its process's RES, so summing the rows would multiply a process's memory by its thread count.--exporter.disable-activity
opnsense_activity_command_threadsGaugecommandNumber of threads running this command. The one signal here that no other exported metric carries: a process leaking threads is invisible everywhere else.--exporter.disable-activity
opnsense_activity_commands_trackedGauge---Number of distinct command labels in the process aggregates this poll, against a cap of 128. At the cap the label set has saturated and new commands are invisible except through the other bucket.--exporter.disable-activity
opnsense_activity_commands_capped_totalCounter---Cumulative count of process-table rows folded into command=\"other\" because the command label set was already at its cap. Zero on any normal firewall; a rising rate means the aggregates are no longer naming everything they measure.--exporter.disable-activity

BPF Statistics

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_bpf_listeners_totalGauge---Total number of active BPF listeners (raw entry count before aggregation)--exporter.disable-bpf
opnsense_bpf_received_packets_totalCounterprocess, interfaceCumulative packets received by BPF listeners for this process/interface pair--exporter.disable-bpf
opnsense_bpf_dropped_packets_totalCounterprocess, interfaceCumulative packets dropped by BPF listeners for this process/interface pair--exporter.disable-bpf
opnsense_bpf_matched_packets_totalCounterprocess, interfaceCumulative packets matched by BPF filter for this process/interface pair--exporter.disable-bpf
opnsense_bpf_store_buffer_bytesGaugeprocess, interfaceCurrent store buffer length in bytes for this process/interface pair--exporter.disable-bpf
opnsense_bpf_hold_buffer_bytesGaugeprocess, interfaceCurrent hold buffer length in bytes for this process/interface pair--exporter.disable-bpf
opnsense_bpf_direction_listenersGaugeprocess, interface, directionNumber of open BPF descriptors for this process/interface/direction. A process holding one descriptor per port (lldpd does) shows one series per port here, which is normal; a count that climbs without the process count changing is a descriptor leak.--exporter.disable-bpf
opnsense_bpf_direction_received_packets_totalCounterprocess, interface, directionCumulative packets received by BPF listeners for this process/interface/direction. Per-direction breakdown of bpf_received_packets_total, which remains the sum across directions.--exporter.disable-bpf
opnsense_bpf_direction_dropped_packets_totalCounterprocess, interface, directionCumulative packets dropped by BPF listeners for this process/interface/direction — the consumer could not read the buffer fast enough, so the capture is incomplete. Per-direction breakdown of bpf_dropped_packets_total.--exporter.disable-bpf
opnsense_bpf_direction_matched_packets_totalCounterprocess, interface, directionCumulative packets matched by the BPF filter for this process/interface/direction. Per-direction breakdown of bpf_matched_packets_total.--exporter.disable-bpf

CARP

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_carp_demotionGauge---CARP demotion level--exporter.disable-carp
opnsense_carp_allowGauge---Whether CARP is allowed (1 = allowed, 0 = not allowed)--exporter.disable-carp
opnsense_carp_maintenance_modeGauge---Whether CARP maintenance mode is enabled (1 = enabled, 0 = disabled)--exporter.disable-carp
opnsense_carp_vips_totalGauge---Total number of CARP Virtual IPs--exporter.disable-carp
opnsense_carp_vip_statusGaugeinterface, vhid, vipCARP VIP status (1 = MASTER, 0 = BACKUP, 2 = INIT, 3 = DISABLED, -1 = unknown)--exporter.disable-carp
opnsense_carp_vip_advbase_secondsGaugeinterface, vhid, vipCARP VIP advertisement base interval in seconds--exporter.disable-carp
opnsense_carp_vip_advskewGaugeinterface, vhid, vipCARP VIP advertisement skew--exporter.disable-carp

CPU

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_cpu_seconds_totalCountermodeCumulative CPU seconds by mode, reconstructed from the api/diagnostics/cpu_usage SSE stream as (percent/100 * measured elapsed time between frames). This is a reconstruction from integer-percentage samples, NOT a kernel tick counter: each sample carries up to ±0.5% quantisation noise, which is unbiased and so does not accumulate systematically. Absent while the stream has been silent for longer than its grace window, because a frozen counter is indistinguishable from an idle CPU. NOTE the difference from node_exporter's identically-named metric: OPNsense reports CPU AGGREGATED ACROSS ALL CORES, not per core, so there is no cpu label and sum(rate(...)) by mode is 1, not the core count. Multiply a rate by 100 to read it as a percentage of the whole machine.--exporter.disable-cpu
opnsense_cpu_stream_upGauge---1 when the CPU usage SSE stream is connected, 0 otherwise. Always exported, including while seconds_total is absent, so a stalled stream is visible rather than inferred.--exporter.disable-cpu
opnsense_cpu_stream_last_frame_age_secondsGauge---Seconds since the last CPU sample was received. Absent until the first frame ever arrives. This is the signal to alert on: the documented failure mode of this endpoint is that keepalives keep flowing after the data has stopped, so connection liveness alone proves nothing.--exporter.disable-cpu
opnsense_cpu_stream_reconnects_totalCounter---Number of times the CPU usage stream has been re-dialled since exporter start. A steadily climbing value means the connection is being torn down repeatedly.--exporter.disable-cpu
opnsense_cpu_stream_frames_totalCounter---Number of CPU samples accepted from the stream since exporter start.--exporter.disable-cpu
opnsense_cpu_stream_counters_publishedGauge---1 when seconds_total is being published, 0 when it has been withdrawn because the stream went silent for longer than the grace window.--exporter.disable-cpu

Captive Portal

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_captiveportal_service_runningGauge---Whether the captive portal service is running (1 = running, 0 = stopped/disabled)--exporter.disable-captiveportal
opnsense_captiveportal_zones_totalGauge---Number of configured captive portal zones (including disabled zones)--exporter.disable-captiveportal
opnsense_captiveportal_sessions_totalGauge---Total number of active captive portal sessions across all zones--exporter.disable-captiveportal
opnsense_captiveportal_zone_sessionsGaugezone_id, zone_descriptionNumber of active captive portal sessions in this zone--exporter.disable-captiveportal
opnsense_captiveportal_vouchersGaugeprovider, group, stateNumber of captive portal vouchers in this group by state (valid, unused, expired)--exporter.disable-captiveportal
opnsense_captiveportal_voucher_group_next_expiry_secondsGaugeprovider, groupUnix timestamp of the earliest hard expiry deadline among this group's unused/valid vouchers (absent when no voucher in the group has one set)--exporter.disable-captiveportal

Certificates

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_certificate_valid_from_secondsGaugedescription, commonname, cert_type, in_useCertificate valid from timestamp in seconds since epoch--exporter.disable-certificates
opnsense_certificate_valid_to_secondsGaugedescription, commonname, cert_type, in_useCertificate valid to (expiry) timestamp in seconds since epoch--exporter.disable-certificates
opnsense_certificate_infoGaugedescription, commonname, cert_type, in_useCertificate information (value is always 1)--exporter.disable-certificates
opnsense_certificate_totalGauge---Total number of certificates--exporter.disable-certificates
opnsense_certificate_ca_valid_from_secondsGaugedescription, commonnameCertificate authority valid from timestamp in seconds since epoch--exporter.disable-certificates
opnsense_certificate_ca_valid_to_secondsGaugedescription, commonnameCertificate authority valid to (expiry) timestamp in seconds since epoch--exporter.disable-certificates
opnsense_certificate_ca_totalGauge---Total number of certificate authorities--exporter.disable-certificates
opnsense_certificate_ca_referencesGaugedescription, commonnameNumber of other configuration objects that reference this certificate authority (OPNsense's own refcount). This is what separates a CA nearing expiry that 50 things depend on - an outage on a known date - from one nothing uses, which is dead config; the validity gauges alone cannot tell them apart. 0 is a real, meaningful value; a CA whose payload carries no refcount at all emits no series.--exporter.disable-certificates

Chrony

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_chrony_service_runningGauge---Whether the Chrony service is running (1 = running, 0 = stopped/disabled)--exporter.disable-chrony
opnsense_chrony_stratumGauge---Stratum of the NTP source chrony is currently tracking--exporter.disable-chrony
opnsense_chrony_system_time_offset_secondsGauge---Current system time offset relative to NTP time in seconds (fast = positive, slow = negative)--exporter.disable-chrony
opnsense_chrony_last_offset_secondsGauge---Estimated local offset on the last clock update in seconds--exporter.disable-chrony
opnsense_chrony_rms_offset_secondsGauge---Long-term average of the offset values in seconds--exporter.disable-chrony
opnsense_chrony_frequency_ppmGauge---Rate at which the system clock would be wrong if chrony was not correcting it (ppm)--exporter.disable-chrony
opnsense_chrony_residual_frequency_ppmGauge---Residual frequency offset of the NTP source (ppm)--exporter.disable-chrony
opnsense_chrony_skew_ppmGauge---Estimated error bound on the frequency (ppm)--exporter.disable-chrony
opnsense_chrony_root_delay_secondsGauge---Total of the network path delays to the stratum-1 NTP server (seconds)--exporter.disable-chrony
opnsense_chrony_root_dispersion_secondsGauge---Total dispersion accumulated through all NTP servers back to stratum-1 (seconds)--exporter.disable-chrony
opnsense_chrony_update_interval_secondsGauge---Interval between the last two clock updates (seconds)--exporter.disable-chrony
opnsense_chrony_leap_statusGauge---Leap second status (0=Normal, 1=Insert second, 2=Delete second, 3=Not synchronised)--exporter.disable-chrony
opnsense_chrony_tracking_infoGaugereference_id, reference_name, leap_statusChrony tracking info label gauge (always 1) - pool-rotation note: source labels churn with pool directives; use instant queries/tables--exporter.disable-chrony
opnsense_chrony_sources_totalGauge---Total number of NTP sources chrony is currently tracking--exporter.disable-chrony
opnsense_chrony_source_selectedGaugesource, modeWhether this source is currently selected as the best available (1 = selected)--exporter.disable-chrony
opnsense_chrony_source_stratumGaugesource, modeStratum of this NTP source--exporter.disable-chrony
opnsense_chrony_source_reachabilityGaugesource, modeSource reachability register (0–255; 255 means all 8 last polls succeeded)--exporter.disable-chrony
opnsense_chrony_source_last_rx_secondsGaugesource, modeTime since the last packet was received from this source (seconds)--exporter.disable-chrony
opnsense_chrony_source_offset_secondsGaugesource, modeMeasured offset of the source (seconds)--exporter.disable-chrony
opnsense_chrony_source_offset_stddev_secondsGaugesourceEstimated standard deviation of the source offset (seconds)--exporter.disable-chrony
opnsense_chrony_source_samplesGaugesourceNumber of samples currently held in the regression (sourcestats)--exporter.disable-chrony

ClamAV

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_clamav_version_infoGaugeversionClamAV engine version information (value is always 1; use labels)--exporter.disable-clamav
opnsense_clamav_signaturesGauge---Total number of ClamAV virus signatures loaded across all databases (0 both when freshclam has never run and when the field is unavailable)--exporter.disable-clamav
opnsense_clamav_signature_db_versionGaugedbVersion number of a ClamAV signature database (increments on every freshclam update), by database--exporter.disable-clamav
opnsense_clamav_signature_db_built_timestamp_secondsGaugedbUnix timestamp of when a ClamAV signature database was built, from the freshclam build metadata, by database. Absent when the build-date fragment could not be parsed.--exporter.disable-clamav

Config Backup

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_backup_last_timestamp_secondsGauge---Unix timestamp of the newest retained config backup. Compare against time() to alert on stale/silent backup failure.--exporter.disable-backup
opnsense_backup_countGauge---Number of config backups OPNsense currently retains.--exporter.disable-backup
opnsense_backup_last_size_bytesGauge---Size in bytes of the newest retained config backup.--exporter.disable-backup

Cron

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_cron_job_statusGaugeuuid, schedule, description, command, originCron job status by name and description (1 = enabled, 0 = disabled)--exporter.disable-cron-table

CrowdSec

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_crowdsec_service_runningGauge---Whether the CrowdSec service is running (1 = running, 0 = stopped/disabled)--exporter.disable-crowdsec
opnsense_crowdsec_alerts_totalGauge---Total number of CrowdSec alerts (count-only, no per-type breakdown)--exporter.disable-crowdsec
opnsense_crowdsec_decisions_totalGauge---Total number of active CrowdSec decisions (count-only, no per-type breakdown)--exporter.disable-crowdsec
opnsense_crowdsec_bouncers_totalGauge---Total number of registered CrowdSec bouncers--exporter.disable-crowdsec
opnsense_crowdsec_bouncer_validGaugename, typeWhether the bouncer API key is valid (1 = valid, 0 = invalid)--exporter.disable-crowdsec
opnsense_crowdsec_bouncer_last_pull_timestamp_secondsGaugename, typeUnix timestamp of the last pull by this bouncer (omitted when never pulled)--exporter.disable-crowdsec
opnsense_crowdsec_machines_totalGauge---Total number of registered CrowdSec machines (agents)--exporter.disable-crowdsec
opnsense_crowdsec_machine_validatedGaugenameWhether the machine registration has been validated (1 = validated, 0 = pending)--exporter.disable-crowdsec
opnsense_crowdsec_machine_last_heartbeat_timestamp_secondsGaugenameUnix timestamp of the last heartbeat from this machine (omitted when absent)--exporter.disable-crowdsec
opnsense_crowdsec_hub_itemsGaugecomponent, statusNumber of installed CrowdSec hub items per component and status (e.g. component=\"scenario\" status=\"enabled,tainted\")--exporter.disable-crowdsec
opnsense_crowdsec_version_infoGaugeversionCrowdSec engine version (value always 1; version is a label)--exporter.disable-crowdsec

Dnsmasq DHCP

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_dnsmasq_leases_totalGauge---Total number of DHCP leases--exporter.disable-dnsmasq
opnsense_dnsmasq_leases_by_interfaceGaugeinterfaceNumber of DHCP leases per interface--exporter.disable-dnsmasq
opnsense_dnsmasq_leases_reserved_totalGauge---Total number of reserved (static) DHCP leases--exporter.disable-dnsmasq
opnsense_dnsmasq_leases_dynamic_totalGauge---Total number of dynamic DHCP leases--exporter.disable-dnsmasq
opnsense_dnsmasq_lease_infoGaugeaddress, hostname, hwaddr, interface, device, vendorPer-lease information (value is expire timestamp). Only emitted when --exporter.enable-dnsmasq-details is set. device is the raw logical interface id and interface the assigned description; they diverge on VLAN children and bridges, and only device joins against the interfaces metrics (the #544 item-5 pattern). vendor is the OUI vendor-name lookup (mirroring the Kea collector's identical field), empty whenever the OUI is unknown.--exporter.disable-dnsmasq
opnsense_dnsmasq_service_runningGauge---Whether the service is running (1 = running, 0 = stopped/disabled)--exporter.disable-dnsmasq
opnsense_dnsmasq_pool_sizeGaugeinterfaceNumber of addresses in the configured dnsmasq DHCP ranges per interface--exporter.disable-dnsmasq

DynDNS

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_dyndns_accounts_totalGauge---Total number of configured DynDNS accounts--exporter.disable-dyndns
opnsense_dyndns_account_enabledGaugedescription, service, hostnames, interfaceWhether this DynDNS account is enabled (1 = enabled, 0 = disabled)--exporter.disable-dyndns
opnsense_dyndns_account_last_update_timestamp_secondsGaugedescription, service, hostnames, interfaceUnix timestamp of the last successful DynDNS IP update for this account--exporter.disable-dyndns
opnsense_dyndns_account_infoGaugedescription, service, hostnames, zone, interface, current_ipDynDNS account information (value is always 1; use labels)--exporter.disable-dyndns
opnsense_dyndns_service_runningGauge---Whether the DynDNS service is running (1 = running, 0 = stopped/disabled)--exporter.disable-dyndns

FRR Routing (BGP/OSPF/BFD)

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_frr_service_runningGauge---Whether the FRR (quagga) service is running (1 = running, 0 = stopped/disabled)--exporter.disable-frr
opnsense_frr_bgp_peers_totalGaugeafTotal number of configured BGP peers for this address family--exporter.disable-frr
opnsense_frr_bgp_failed_peersGaugeafNumber of BGP peers in a failed state for this address family--exporter.disable-frr
opnsense_frr_bgp_rib_entriesGaugeafNumber of RIB entries for this address family--exporter.disable-frr
opnsense_frr_bgp_peer_upGaugepeer, remote_as, afWhether this BGP peer session is established (1 = Established, 0 = otherwise)--exporter.disable-frr
opnsense_frr_bgp_peer_prefixes_receivedGaugepeer, remote_as, afNumber of prefixes received from this BGP peer--exporter.disable-frr
opnsense_frr_bgp_peer_prefixes_sentGaugepeer, remote_as, afNumber of prefixes sent to this BGP peer--exporter.disable-frr
opnsense_frr_bgp_peer_uptime_secondsGaugepeer, remote_as, afUptime of this BGP peer session in seconds--exporter.disable-frr
opnsense_frr_bgp_peer_messages_received_totalCounterpeer, remote_as, afCumulative BGP messages received from this peer--exporter.disable-frr
opnsense_frr_bgp_peer_messages_sent_totalCounterpeer, remote_as, afCumulative BGP messages sent to this peer--exporter.disable-frr
opnsense_frr_bgp_peer_connections_established_totalCounterpeerCumulative number of times this BGP peer session has been established (flap counter)--exporter.disable-frr
opnsense_frr_bgp_peer_connections_dropped_totalCounterpeerCumulative number of times this BGP peer session has been dropped (flap counter)--exporter.disable-frr
opnsense_frr_bgp_peer_messages_by_type_totalCounterpeer, type, directionCumulative BGP messages exchanged with this peer, by message type and direction--exporter.disable-frr
opnsense_frr_bgp_peer_last_reset_secondsGaugepeerTime since this BGP peer session was last reset, in seconds. The reset reason (FRR's lastResetDueTo) is logged at debug level rather than carried as a label (unbounded free-text value).--exporter.disable-frr
opnsense_frr_bgp_peer_prefixes_acceptedGaugepeer, afNumber of prefixes accepted from this BGP peer after inbound policy, by address family (complements the pre-policy opnsense_frr_bgp_peer_prefixes_received)--exporter.disable-frr
opnsense_frr_bgp_peer_queue_depthGaugepeer, directionCurrent BGP work-queue depth for this peer, by direction (in = inbound, out = outbound)--exporter.disable-frr
opnsense_frr_ospf_neighbors_totalGauge---Total number of OSPF neighbors--exporter.disable-frr
opnsense_frr_ospf_neighbor_adjacencyGaugeneighbor_id, address, interfaceWhether this OSPF neighbor is in Full adjacency state (1 = Full, 0 = otherwise)--exporter.disable-frr
opnsense_frr_ospf_neighbor_nsm_state_infoGaugeneighbor_id, address, interface, nsm_stateFRR's raw OSPF neighbor state machine state for this neighbor (value is always 1; use the nsm_state label — DependUpon/Deleted/Down/Attempt/Init/2-Way/ExStart/Exchange/Loading/Full). Distinct from ospf_neighbor_adjacency: that flag only shows Full vs. not, this shows what a non-Full neighbor is stuck at.--exporter.disable-frr
opnsense_frr_ospf_neighbor_uptime_secondsGaugeneighbor_id, address, interfaceHow long this OSPF neighbor's adjacency has been progressing, in seconds. Absent (not zero) while the neighbor's inactivity timer isn't scheduled — FRR doesn't report this field for a neighbor that hasn't started forming an adjacency.--exporter.disable-frr
opnsense_frr_ospf_neighbor_dead_timer_secondsGaugeneighbor_id, address, interfaceTime remaining before this OSPF neighbor's dead interval expires, in seconds. A value repeatedly resetting close to the configured dead interval is healthy; one trending toward zero and recovering (rather than resetting cleanly on a fresh hello) is a flapping adjacency. Absent (not zero) when FRR reports the timer as \"inactive\" (no dead-interval timer currently running for this neighbor).--exporter.disable-frr
opnsense_frr_ospf_neighbor_lsa_queue_depthGaugeneighbor_id, address, interface, queueCurrent LSA synchronization queue depth for this OSPF neighbor, by queue (db_summary = database description summary list, ls_request = link-state request list, ls_retransmission = link-state retransmission list). Persistently nonzero ls_retransmission depth is the classic symptom of a neighbor stuck failing to acknowledge LSAs.--exporter.disable-frr
opnsense_frr_ospf_area_interfaces_activeGaugeareaNumber of active interfaces in this OSPF area--exporter.disable-frr
opnsense_frr_ospf_area_neighbors_full_adjacentGaugeareaNumber of neighbors in Full adjacency state in this OSPF area--exporter.disable-frr
opnsense_frr_ospf_area_lsa_countGaugeareaNumber of LSAs in this OSPF area--exporter.disable-frr
opnsense_frr_ospf_area_spf_executed_totalCounterareaCumulative number of SPF calculations executed in this OSPF area--exporter.disable-frr
opnsense_frr_ospf_spf_last_executed_timestamp_secondsGauge---Unix timestamp of this OSPF instance's last SPF calculation. Exported as an absolute timestamp rather than a \"seconds ago\" reading (FRR re-derives the age fresh on every poll; an absolute timestamp lets time()-metric compute current age at query time instead of only at last-scrape time). Absent until the instance's first SPF run.--exporter.disable-frr
opnsense_frr_ospf_spf_last_duration_secondsGauge---Wall-clock duration of this OSPF instance's last SPF calculation, in seconds. Absent until the instance's first SPF run.--exporter.disable-frr
opnsense_frr_ospf_interface_upGaugeinterface, areaWhether this OSPF interface's underlying link is up (1 = up, 0 = down)--exporter.disable-frr
opnsense_frr_ospf_interface_costGaugeinterface, areaOSPF cost configured on this interface--exporter.disable-frr
opnsense_frr_ospf_interface_neighborsGaugeinterface, areaNumber of OSPF neighbors seen on this interface--exporter.disable-frr
opnsense_frr_ospf_interface_neighbors_adjacentGaugeinterface, areaNumber of OSPF neighbors in Full adjacency on this interface--exporter.disable-frr
opnsense_frr_ospf_interface_stateGaugeinterface, stateOSPF interface state (enum-style: 1 for the current state, 0 for the others in the fixed set DR/BDR/DROther/PointToPoint/Waiting/Down)--exporter.disable-frr
opnsense_frr_ospfv3_interface_upGaugeinterface, areaWhether this OSPFv3 interface is up (1 = up, 0 = down)--exporter.disable-frr
opnsense_frr_ospfv3_interface_costGaugeinterface, areaOSPFv3 cost configured on this interface--exporter.disable-frr
opnsense_frr_ospfv3_interface_stateGaugeinterface, stateOSPFv3 interface state (enum-style: 1 for the current state, 0 for the others in the fixed set DR/BDR/DROther/PointToPoint/Waiting/Down). There is no OSPFv3 neighbor endpoint in the quagga plugin API, so interface state is the best available proxy for adjacency.--exporter.disable-frr
opnsense_frr_ospfv3_area_lsa_countGaugeareaNumber of LSAs in this OSPFv3 area (v3 parity with opnsense_frr_ospf_area_lsa_count)--exporter.disable-frr
opnsense_frr_ospfv3_interface_pending_lsaGaugeinterface, queueOSPFv3 flooding backlog on this interface: pending LSAs by queue (update = LSUpdate, ack = LSAck)--exporter.disable-frr
opnsense_frr_ospfv3_spf_last_duration_secondsGauge---Wall-clock duration of this OSPFv3 instance's last SPF calculation, in seconds. Absent until the instance's first SPF run.--exporter.disable-frr
opnsense_frr_ospfv3_area_spf_last_executed_timestamp_secondsGaugeareaUnix timestamp of this OSPFv3 area's last SPF calculation. Exported as an absolute timestamp for the same reason as opnsense_frr_ospf_spf_last_executed_timestamp_seconds above. Absent until this area's first SPF run.--exporter.disable-frr
opnsense_frr_bfd_peers_totalGauge---Total number of configured BFD peers--exporter.disable-frr
opnsense_frr_bfd_peer_upGaugepeer, interfaceWhether this BFD peer session is up (1 = up, 0 = down)--exporter.disable-frr
opnsense_frr_bfd_peer_uptime_secondsGaugepeerUptime of this BFD peer session in seconds--exporter.disable-frr
opnsense_frr_bfd_peer_control_packets_received_totalCounterpeerCumulative BFD control packets received from this peer--exporter.disable-frr
opnsense_frr_bfd_peer_control_packets_sent_totalCounterpeerCumulative BFD control packets sent to this peer--exporter.disable-frr
opnsense_frr_bfd_peer_session_up_events_totalCounterpeerCumulative BFD session-up events for this peer--exporter.disable-frr
opnsense_frr_bfd_peer_session_down_events_totalCounterpeerCumulative BFD session-down events for this peer--exporter.disable-frr
opnsense_frr_bfd_peer_diagnostic_infoGaugepeer, diagnostic, remote_diagnosticFRR BFD peer diagnostic reason (value is always 1; use labels). diagnostic/remote_diagnostic are FRR's diag2str() enum: ok, control detection time expired, echo function failed, neighbor signaled session down, forwarding plane reset, path down, concatenated path down, administratively down, reverse concatenated path down, unknown.--exporter.disable-frr
opnsense_frr_bfd_peer_rtt_min_microsecondsGaugepeerMinimum measured BFD round-trip time for this peer, in microseconds. Reads 0 unless both ends support FRR's BFD RTT extension — a legitimate zero, not a fault.--exporter.disable-frr
opnsense_frr_bfd_peer_rtt_avg_microsecondsGaugepeerAverage measured BFD round-trip time for this peer, in microseconds. Reads 0 unless both ends support FRR's BFD RTT extension — a legitimate zero, not a fault.--exporter.disable-frr
opnsense_frr_bfd_peer_rtt_max_microsecondsGaugepeerMaximum measured BFD round-trip time for this peer, in microseconds. Reads 0 unless both ends support FRR's BFD RTT extension — a legitimate zero, not a fault.--exporter.disable-frr
opnsense_frr_bfd_peer_downtime_secondsGaugepeerDuration this BFD peer session has been down, in seconds. Only emitted while the peer is in the down state — absent (not zero) while up, initializing, or administratively shut down, matching FRR's mutually exclusive uptime/downtime fields.--exporter.disable-frr
opnsense_frr_route_countGaugeaf, protocolNumber of distinct routed prefixes in the zebra RIB, by address family and protocol. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr
opnsense_frr_route_nexthop_countGaugeaf, protocolNumber of nexthop rows in the zebra RIB (ECMP width), by address family and protocol. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr
opnsense_frr_ospf_route_countGaugetypeNumber of rows in the OSPF route table, by route type. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr
opnsense_frr_ospfv3_route_countGaugetypeNumber of rows in the OSPFv3 route table, by route type. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr
opnsense_frr_ospf_lsa_countGaugearea, lsa_typeNumber of LSAs in the OSPF LSDB, by area and LSA type. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr
opnsense_frr_ospfv3_lsa_countGaugescopeNumber of LSAs in the OSPFv3 LSDB, by flooding scope. Only emitted when --exporter.enable-frr-routes is set.--exporter.disable-frr

Feature Availability

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_feature_availableGaugefeature, enabledWhether a plugin-gated OPNsense feature is installed on this firewall: 1 if its API endpoint answered, 0 if it returned 404 (the plugin is absent). The series is present for every plugin-gated collector family, so a 0 means 'this box does not have it' and ABSENCE means only that availability has never been determined - an unreachable firewall leaves the previous verdict in place rather than reporting everything as gone. enabled reflects whether that collector is currently switched on, so available=1 with enabled=\"false\" is the actionable state: a plugin you have but are not scraping, whether it is an opt-in collector or a default-on one you disabled. Refreshed on the cold poll tier (15m) independent of --exporter.cache-ttl, and a collector that is already enabled is not re-probed - its own polling answers the same question (#517, #525).--exporter.disable-feature-availability

Firewall

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_firewall_in_ipv4_pass_packets_totalCounterinterfaceThe number of IPv4 incoming packets that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv4_pass_packets_totalCounterinterfaceThe number of IPv4 outgoing packets that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv4_block_packets_totalCounterinterfaceThe number of IPv4 incoming packets that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv4_block_packets_totalCounterinterfaceThe number of IPv4 outgoing packets that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv6_pass_packets_totalCounterinterfaceThe number of IPv6 incoming packets that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv6_pass_packets_totalCounterinterfaceThe number of IPv6 outgoing packets that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv6_block_packets_totalCounterinterfaceThe number of IPv6 incoming packets that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv6_block_packets_totalCounterinterfaceThe number of IPv6 outgoing packets that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv4_pass_bytes_totalCounterinterfaceThe number of IPv4 incoming bytes that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv4_pass_bytes_totalCounterinterfaceThe number of IPv4 outgoing bytes that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv4_block_bytes_totalCounterinterfaceThe number of IPv4 incoming bytes that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv4_block_bytes_totalCounterinterfaceThe number of IPv4 outgoing bytes that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv6_pass_bytes_totalCounterinterfaceThe number of IPv6 incoming bytes that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv6_pass_bytes_totalCounterinterfaceThe number of IPv6 outgoing bytes that were allowed to pass through the firewall by interface--exporter.disable-firewall
opnsense_firewall_in_ipv6_block_bytes_totalCounterinterfaceThe number of IPv6 incoming bytes that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_out_ipv6_block_bytes_totalCounterinterfaceThe number of IPv6 outgoing bytes that were blocked by the firewall by interface--exporter.disable-firewall
opnsense_firewall_pf_states_currentGauge---Current number of active PF states--exporter.disable-firewall
opnsense_firewall_pf_states_limitGauge---Maximum number of PF states allowed--exporter.disable-firewall
opnsense_firewall_pf_interface_referencesGaugeinterface, skippedNumber of PF state-table entries currently referencing each interface — the per-interface breakdown of the global pf_states_current. A live depth, not a cumulative counter: it rises and falls with connection count, so never wrap it in rate()/increase(). skipped=\"true\" means pf \"skip on interface\" is enabled for that device, so pf is not filtering it at all — the reference count is still real, but no rules are being evaluated. The pfctl \" (skip)\" name suffix is stripped out of the interface label so toggling that option does not rename the series. The pfctl \"all\" aggregate row is deliberately NOT emitted: it is the sum of every other row, so including it would double every total in any panel that aggregates this family — use sum() in PromQL instead. Do not add it back.--exporter.disable-firewall
opnsense_firewall_interface_log_entries_recentGaugeinterfaceFirewall log entries per interface within the most recent ~5000-record log window (sliding, not a counter; interface=\"other\" is an aggregate of interfaces beyond the top 10)--exporter.disable-firewall
opnsense_firewall_geoip_alias_usagesGauge---Number of configured firewall aliases of type GeoIP, regardless of whether the GeoIP database itself has ever downloaded--exporter.disable-firewall
opnsense_firewall_geoip_addressesGauge---Number of GeoIP addresses/networks currently loaded from the downloaded database (0 until a MaxMind/ipinfo key is configured and a download has succeeded)--exporter.disable-firewall
opnsense_firewall_geoip_filesGauge---Number of per-country GeoIP alias table files currently written (0 until a MaxMind/ipinfo key is configured and a download has succeeded)--exporter.disable-firewall
opnsense_firewall_geoip_last_update_timestamp_secondsGauge---Unix timestamp of the last successful GeoIP database download. Absent until the first successful download; compare against time() to alert on a stale/failed GeoIP database (e.g. an expired MaxMind license key).--exporter.disable-firewall
opnsense_firewall_pf_counters_cleared_timestamp_secondsGaugeinterfaceUnix timestamp (UTC) of pf's last counters reset for this interface — pfctl's own \"Cleared\" marker, the same instant a pfctl -z or a filter/rule reload zeroed the pass/block packet and byte counters above for that interface. OPNsense reports this timestamp with no timezone marker at all, so it is decoded assuming UTC and can be off by the firewall's real UTC offset — treat this as \"a reset happened\" (a step change, or a value newer than your rate() window), not a to-the-minute clock. A rate()/increase() query spanning a reset already reads a bogus negative delta or spurious plateau on the pass/block counters above; use this timestamp to explain that away rather than chase it as a real traffic drop. Absent entirely when the box has never reported a parseable value for this interface.--exporter.disable-firewall
opnsense_firewall_nat_rulesGaugetype, enabledNumber of MVC-managed NAT rules by type and enabled state (source_nat, d_nat, one_to_one, npt). Rules created before an admin migrated to the MVC-managed NAT backend are not counted.--exporter.disable-firewall

Firewall Aliases

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_alias_tables_totalGauge---Total number of pf alias tables--exporter.disable-alias
opnsense_alias_table_entriesGaugetableCurrent number of entries in this pf alias table--exporter.disable-alias
opnsense_alias_table_entries_usedGauge---Number of pf table-entries slots currently in use (global)--exporter.disable-alias
opnsense_alias_table_entries_limitGauge---Maximum number of pf table-entries slots (global)--exporter.disable-alias
opnsense_alias_table_evaluations_totalCountertable, resultPacket evaluations against this alias table since last reset--exporter.disable-alias
opnsense_alias_table_packets_totalCountertable, direction, actionPackets matched against this alias table since last reset--exporter.disable-alias
opnsense_alias_table_bytes_totalCountertable, direction, actionBytes matched against this alias table since last reset--exporter.disable-alias
opnsense_alias_table_updated_timestamp_secondsGaugetableUnix timestamp of the last time this alias table's persisted content was written - i.e. when a DNS- or URL-backed alias (a threat feed, say) last refreshed. A feed that silently stops refreshing is a security control failing open, and no other metric can see it: the table still holds its stale rows, so table_entries looks healthy. Only emitted for tables that HAVE persisted content; a static host/network alias has no refresh cycle and emits no series rather than a misleading epoch 0. Derived from the file mtime as a timezone-less local timestamp and read as UTC, so the absolute value can be off by the firewall's UTC offset - compare ages, not wall clocks.--exporter.disable-alias

Firewall Rules

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_firewall_rule_rules_totalGauge---Total number of firewall rules with statistics--exporter.disable-firewall-rules
opnsense_firewall_rule_evaluations_totalCounteruuid, description, action, interface, direction, protocolTotal number of rule evaluations per firewall rule--exporter.disable-firewall-rules
opnsense_firewall_rule_packets_totalCounteruuid, description, action, interface, direction, protocolTotal number of packets matched per firewall rule--exporter.disable-firewall-rules
opnsense_firewall_rule_bytes_totalCounteruuid, description, action, interface, direction, protocolTotal number of bytes matched per firewall rule--exporter.disable-firewall-rules
opnsense_firewall_rule_statesGaugeuuid, description, action, interface, direction, protocolCurrent number of active states per firewall rule--exporter.disable-firewall-rules
opnsense_firewall_rule_pf_rulesGaugeuuid, description, action, interface, direction, protocolNumber of PF rules generated per firewall rule--exporter.disable-firewall-rules
opnsense_firewall_rule_configured_rulesGaugeenabledNumber of configured firewall filter rules by enabled state. Only emitted when --exporter.enable-firewall-rules-details is set.--exporter.disable-firewall-rules

Firmware

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_firmware_infoGaugeos_version, product_version, product_id, product_abiOPNsense firmware information--exporter.disable-firmware
opnsense_firmware_needs_rebootGauge---Whether applying the currently AVAILABLE update would require a reboot, or a plugin has set the reboot-required hook (1 = yes, 0 = no). This tracks update availability, NOT a completed-but-unapplied install - it can be 1 for days before anything is installed and is not cleared by rebooting. For the major-version-upgrade reboot signal see upgrade_needs_reboot.--exporter.disable-firmware
opnsense_firmware_upgrade_needs_rebootGauge---Whether a pending major-version upgrade (product.product_check) requires a reboot to apply (1 = yes, 0 = no). Distinct from needs_reboot, which tracks base/kernel/plugin update availability.--exporter.disable-firmware
opnsense_firmware_last_check_timestamp_secondsGauge---Unix timestamp of the last firmware update check--exporter.disable-firmware
opnsense_firmware_new_packages_countGauge---Number of new packages available--exporter.disable-firmware
opnsense_firmware_upgrade_packages_countGauge---Number of packages with available upgrades--exporter.disable-firmware
opnsense_firmware_downgrade_packages_countGauge---Number of packages available to downgrade--exporter.disable-firmware
opnsense_firmware_reinstall_packages_countGauge---Number of packages available to reinstall--exporter.disable-firmware
opnsense_firmware_remove_packages_countGauge---Number of packages the pending update would remove--exporter.disable-firmware
opnsense_firmware_upgrade_sets_countGauge---Number of pending upgrade sets (the synthetic base/kernel entries of a major or point upgrade, not ordinary packages)--exporter.disable-firmware
opnsense_firmware_update_check_successGauge---Whether the firewall's stored update check actually succeeded (1 = the repository was reachable, authenticated and verified; 0 = it was not). Only emitted once a check has been stored. This is NOT the same as \"no updates pending\": before this metric existed, a DNS failure, expired subscription, revoked fingerprint or unavailable release train looked exactly like a healthy check with zero updates. Reflects the STORED result of the box's own check (refreshed roughly daily) as seen through the exporter's firmware response cache, so a state change can take up to --exporter.firmware-cache-ttl (default 12h) to appear.--exporter.disable-firmware
opnsense_firmware_update_check_stateGaugecomponent, stateCurrent state of one component of the firewall's stored update check (always 1; exactly one series per component). component is connection or repository. state is drawn from OPNsense's closed vocabularies - connection: error/unauthenticated/misconfigured/unresolved/ok, repository: error/untrusted/unsigned/revoked/incomplete/forbidden/ok - and anything else, including a future upstream state, collapses to unknown. Only emitted once a check has been stored.--exporter.disable-firmware
opnsense_firmware_pending_download_bytesGauge---Total size in bytes the pending update would download, parsed from the stored check's mixed-unit download_size list (base-2 units). Only emitted once a check has been stored AND the field parsed unambiguously - a value that cannot be parsed emits no series rather than a fabricated 0. Unlike the OPNsense GUI, which truncates a fractional size, fractions are kept, so this can read slightly higher than the number the GUI displays.--exporter.disable-firmware
opnsense_firmware_package_update_availableGaugename, installed_version, new_versionPending package update (1 = update available). Only emitted when --exporter.enable-firmware-package-details is set.--exporter.disable-firmware
opnsense_firmware_plugin_installedGaugename, versionInstalled OPNsense plugin (1 = installed). Only emitted when --exporter.enable-firmware-package-details is set.--exporter.disable-firmware
opnsense_firmware_major_upgrade_availableGauge---Whether a MAJOR release upgrade is on offer (1 = yes), e.g. 26.1 to 26.7. This is a different maintenance decision from the package updates upgrade_packages_count tracks - a scheduled-window job, not something to apply mid-afternoon - and upgrade_needs_reboot describes THIS upgrade, not the package ones. Only emitted once the box has a stored update check; before that there is nothing to report and a 0 would claim 'no major upgrade pending' on a firewall that has never looked.--exporter.disable-firmware
opnsense_firmware_major_upgrade_infoGaugeversionThe release a pending major upgrade would move this firewall to (always 1; read the version label). Emitted ONLY while such an upgrade is on offer, so the series appearing is itself the signal and there is never a stale version=\"\" series hanging around. Cardinality is one series, changing at most once or twice a year.--exporter.disable-firmware
opnsense_firmware_plugin_size_bytesGaugename, versionInstalled size of this OPNsense plugin, in bytes. Only emitted when --exporter.enable-firmware-package-details is set. APPROXIMATE: OPNsense reports the size as an already-humanised string with one decimal place (pkg's %sh, then formatBytes), so this is that display value converted to base-2 bytes, not the exact on-disk size - good for attributing disk pressure between plugins, not for accounting. A plugin whose size upstream could not report emits no series rather than 0.--exporter.disable-firmware
opnsense_firmware_plugin_lockedGaugename, versionWhether this plugin is pkg-locked against updates (1 = locked). Only emitted when --exporter.enable-firmware-package-details is set. This is the explanation for a plugin that sits at an old version while everything else moves - a locked plugin is skipped by an upgrade rather than failing it.--exporter.disable-firmware
opnsense_firmware_plugin_automaticGaugename, versionWhether this plugin was installed automatically as a dependency rather than chosen deliberately (1 = automatic). Only emitted when --exporter.enable-firmware-package-details is set.--exporter.disable-firmware

Flow Volume

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_flow_bytes_totalCounterinterface, direction, transport, category, action, source, scope, countryBytes observed in flow records, by bounded dimension. Keys beyond --flow.top-n fold into other, which preserves the source label, so the family still sums exactly at any limit. A series that leaves the top-N and later returns resumes from the volume it accumulated while folded, so it reads as a counter reset - deliberate, since the alternative is freezing it at its last value forever. From phase 2 this family carries BOTH sources' measurement of the same traffic: pin source= in any query or it double-counts. IPs, ports, hostnames, application names, domains and connection ids are never labels; they stay as structured metadata on the shipped record. The country label is populated by default (#537) and names the REMOTE end of the flow whichever end that is; it reads empty where GeoIP cannot answer, and on a deployment without --geoip.enabled it is empty on every series. Set --flow.geoip.metric-dims=false to drop it entirely.--exporter.disable-flow
opnsense_flow_packets_totalCounterinterface, direction, transport, category, action, source, scope, countryPackets observed in flow records, by bounded dimension. Same folding, reset and cross-source semantics as opnsense_flow_bytes_total.--exporter.disable-flow
opnsense_flow_records_totalCounterinterface, direction, transport, category, action, source, scope, countryFlow records observed, by bounded dimension. Counts records, not connections: a Zenarmor conn document is one per connection, but a NetFlow connection produces several records.--exporter.disable-flow
opnsense_flow_payload_byte_fallback_totalCounter---Flow records whose byte count came from Zenarmor's payload counter because its wire counter read zero. Zenarmor only accumulates wire bytes once it has tracked a flow past its first packets, so short UDP flows (DNS, STUN, SSDP) report zero; without the fallback those records would count toward records_total with no bytes at all.--exporter.disable-flow
opnsense_flow_interface_unresolved_totalCounter---Flow records emitted with interface=\"unresolved\" because the interface table had not arrived yet. The source states a kernel device; the DESCRIPTION comes from the enrichment snapshot, which lands on the exporter's own schedule, so a push lane can ingest for minutes before it can label anything. Emitting the raw device instead would invent a SECOND series for an interface that already has one and make every sum by (interface) under-report both (#606). This is a startup artifact and it closes on its own: on the reference box every such record landed with the process less than 300 seconds old, across 7 days and 51 restarts. A rate that continues past that means the interface fetch itself is failing, not that a restart happened.--exporter.disable-flow
opnsense_flow_rollup_keysGauge---Distinct label combinations currently tracked by the flow rollup accumulator.--exporter.disable-flow
opnsense_flow_rollup_keys_maxGauge---Configured ceiling on tracked label combinations (--flow.max-keys); 0 means unbounded. At the ceiling every NEW combination folds into other indefinitely, so compare against opnsense_flow_rollup_keys to see saturation coming.--exporter.disable-flow
opnsense_flow_rollup_top_nGauge---Configured ceiling on emitted series (--flow.top-n); 0 means unbounded.--exporter.disable-flow
opnsense_flow_rollup_keys_foldedGauge---Tracked label combinations currently outside the top-N and therefore folded into other rather than emitted individually.--exporter.disable-flow
opnsense_flow_rollup_capped_totalCounter---Flow records folded into other because the accumulator was already at --flow.max-keys when their label combination first appeared. A rising value means new dimensions are being lost to the cap, not merely folded by the top-N.--exporter.disable-flow
opnsense_flow_unique_destinationsGaugeinterfaceDistinct destination addresses seen per interface - a bounded stand-in for a per-destination series (one gauge per interface, never one per destination). A set, not a sum, so a destination reported by both the NetFlow and Zenarmor lanes counts once. Saturates at an internal per-interface cap; a value pinned at the cap means the true count is at least that high, which is itself a scanning/fan-out signal.--exporter.disable-flow
opnsense_flow_unique_destinations_capped_totalCounter---Observations folded into the other interface bucket because a previously unseen interface label arrived when the distinct-destination tracker was already at its interface budget. The interface and VLAN strings are chosen by the Zenarmor sender, so without an outer bound an admitted sender could mint one map and one series per novel combination (#563). A rising value means interface labels beyond the budget are being merged, so per-interface distinct-destination counts are no longer complete - either the box genuinely has more interfaces than the budget, or a sender is inventing them.--exporter.disable-flow
opnsense_flow_top_talker_bytes_totalCounterhost, directionBytes per internal host and direction, top-N with an other remainder per direction so a sum-by-direction stays exact. OPT-IN behind --flow.top-talkers because the host label is unbounded cardinality the other flow metrics refuse. Counts a single source, so on a box running both lanes a host's bytes are not doubled; it therefore has no source label. A host that leaves and re-enters the top-N reads as a counter reset on that one series.--exporter.disable-flow
opnsense_flow_source_byte_delta_ratioGaugeinterfaceHistogram of NetFlow-over-Zenarmor byte ratios on merged flow records, by interface - the payoff of correlating the two sources (#346 decision 3). 1.0 is agreement; a value well above 1 means Zenarmor inspected far fewer bytes than crossed the wire, which is a security signal, not an error. Present only where both lanes run and correlate (--flow.log-mode=per_flow); absent otherwise, since there is no disagreement to measure. READ THE DEVIATION CAREFULLY: it is not all source disagreement (#604). NetFlow counts WIRE bytes; Zenarmor falls back to PAYLOAD bytes on roughly half of all flow records (short UDP, where it has not yet accumulated wire bytes), so on that population the histogram is reporting per-packet header overhead - the gap clusters at 28 bytes, the IPv4 IP+UDP header, and the p90 there alone reaches 1.96. The record states its own basis, so a consumer can tell the two apart. Window partials - where this window's NetFlow bytes would be compared against a whole connection's Zenarmor bytes - are EXCLUDED entirely and counted in source_byte_delta_excluded_total, so the impossible sub-1.0 tail is gone rather than explained away. For the underlying security question (is traffic evading inspection) prefer a BYTE-WEIGHTED comparison - the ratio of summed bytes on merged records - over a percentile of per-flow ratios.--exporter.disable-flow
opnsense_flow_source_byte_delta_excluded_totalCounter---Merged flow records deliberately kept OUT of source_byte_delta_ratio because their two sides are not totalling the same thing (#604). Today that is exactly the window-partial case: corrKey buckets by flow-end, so a connection longer than --flow.correlate.window emits one record per window carrying only THAT window's NetFlow bytes, while the single Zenarmor conn document carries the whole connection's counters and merges into one of them. Comparing those reads as \"the firewall counted 75x fewer bytes than crossed the wire\", which is impossible. The records still SHIP with both sides' counters - only the comparison is meaningless - so this is the histogram's coverage gap, not lost volume. On a box with a long active timeout expect a steady non-zero rate; a sudden rise means connections are outliving the correlate window more often.--exporter.disable-flow
opnsense_flow_dns_cache_entriesGauge---Answers currently held in the DNS answer cache, which gives a flow to a bare IP its dst.domain (§7). Compare against --flow.dns-cache.size to see it approaching the insert cap.--exporter.disable-flow
opnsense_flow_dns_cache_hits_totalCounter---DNS answer-cache lookups that resolved a domain for a flow's destination.--exporter.disable-flow
opnsense_flow_dns_cache_misses_totalCounter---DNS answer-cache lookups with no cached (or a TTL-expired) answer. High against hits is normal for a mostly-IP workload; it is the denominator that tells a cold cache from a thrashing one.--exporter.disable-flow
opnsense_flow_dns_cache_rejected_totalCounter---DNS answers refused insertion because the cache was already at --flow.dns-cache.size. Over the cap it stops inserting rather than evicting hot entries, so a rising value means the cap is binding and domain enrichment is going stale for new answers.--exporter.disable-flow
opnsense_flow_geoip_lookups_totalCounterdatabase, resultGeoIP enrichment lookups, by database (\"country\", \"asn\" or \"skipped\") and result. \"hit\" means a record was found; \"miss\" means the loaded database has no record for that address; \"skipped\" (always with database=\"skipped\") counts addresses that never reached a database because they are not globally routable - loopback, RFC 1918, link-local, and carrier-grade NAT, which MaxMind publishes no records for at all. A database that is not loaded contributes neither hits nor misses, so a country hit rate of zero with a non-zero ASN rate means the country database specifically failed to load.--exporter.disable-flow
opnsense_flow_geoip_reloads_totalCounterdatabase, resultGeoIP database hot-swaps, by database and result. A \"success\" means a changed file on disk was read and swapped in atomically, which is how an operator's geoipupdate cron or the built-in downloader replaces a database under a running exporter. A \"failure\" means the new file could not be read or parsed, in which case the PREVIOUSLY loaded database keeps serving - a bad update costs freshness, never availability. An unchanged file is not counted at all. Failures are not attributable to one database without threading the path out of the reload, so they carry database=\"skipped\", a value no real database uses.--exporter.disable-flow
opnsense_flow_geoip_downloads_totalCounterresultMaxMind database downloads, by result: \"updated\" (a newer build was fetched, verified against its published SHA-256, and installed), \"unmodified\" (the conditional request returned 304 - the healthy steady state, and what keeps a daily updater inside MaxMind's download limit), or \"failure\". Emitted only when --geoip.download.enabled is set; an operator-managed deployment leaves this at zero forever, which is correct.--exporter.disable-flow
opnsense_flow_geoip_database_build_timestamp_secondsGaugedatabaseUnix timestamp of the loaded GeoIP database's build, per database. This is the publisher's BUILD date, not when the file was downloaded, so it is the right thing to alert staleness on: time() - this > 45d catches a refresh that has silently stopped, which the fail-open design makes otherwise invisible. 45d rather than something tighter because it has to cover every database the exporter can load, and the DB-IP Lite copy bundled in the image republishes monthly while GeoLite2 rebuilds twice a week. ABSENT for a database that is not loaded, rather than zero - a zero would read as \"built in 1970\" and fire every staleness alert ever written against it.--exporter.disable-flow
opnsense_flow_geoip_enriched_records_totalCounter---Flow records that gained at least one fact from OUR database. It is the \"is this feature doing anything\" signal, and it is what distinguishes a database that failed to load from a network whose traffic is genuinely all internal - both of which otherwise look like silence, because enrichment is fail-open by design.--exporter.disable-flow
opnsense_flow_geoip_country_comparisons_totalCounterresultFlow endpoints where BOTH our database and Zenarmor's answered with a country, by whether they agreed. This is what makes the cost of the ours-wins precedence rule measurable rather than assumed: Zenarmor's database is a commercial GeoIP2-City build (verified on a live firewall - database_type \"GeoIP2-City\", 126 MB against a stock GeoLite2's 63 MB), so overwriting its answer with a free GeoLite2 lookup can genuinely replace a better attribution with a worse one. A rising disagree rate is the signal to look; ours still wins the exported country either way, and the disagreeing value is kept on the log record as <srcdst>.geo.zen_country. Absent entirely without Zenarmor, since there is nothing to compare against.
opnsense_flow_geoip_merge_address_mismatches_totalCounter---Correlator merges where the Zenarmor record and the NetFlow record could not be lined up in EITHER orientation, so no geo was folded across (#647). Expected at or near zero: the correlator paired the two records by connection key, so their naming the same two addresses is the premise of the merge rather than a hope. A climbing value means that premise is false and merged records are silently missing Zenarmor's contribution. It is counted because the alternative this replaced was worse and invisible - endpoints were previously paired by POSITION, so a Zenarmor document describing the connection from the initiator's side attached each end's geo to the opposite address, which on a private destination produced a fabricated country carrying a maxmind provenance for an address MaxMind categorically declines to look up.--exporter.disable-flow
opnsense_flow_correlator_entriesGauge---Connection-windows the correlator is currently holding, waiting for their window to elapse or a Zenarmor conn document to arrive.--exporter.disable-flow
opnsense_flow_correlator_emitted_totalCounter---Flow-log records the correlator has emitted: NetFlow fragments collapsed into one record per connection-window, merged with Zenarmor L7 where a conn document matched.--exporter.disable-flow
opnsense_flow_correlator_matched_totalCounter---Subset of emitted records that carried Zenarmor enrichment (source=merged). Against correlator_emitted_total this is the join hit-rate, which #346 shows is materially lower for long flows whose NetFlow records arrive up to ~30m after the connection ended.--exporter.disable-flow
opnsense_flow_correlator_evicted_totalCounter---Entries force-emitted early because the map hit --flow.correlate.max-entries. A forced emit loses no bytes, but a rising rate means the cap is binding under load and should be raised.--exporter.disable-flow
opnsense_flow_correlator_expired_totalCounter---Entries emitted on the normal window-expiry path (the healthy path, as opposed to eviction).--exporter.disable-flow
opnsense_flow_correlator_enrichment_overwrites_totalCounter---A second Zenarmor conn document landing for a key that already held one (#590). The second REPLACES the first wholesale rather than merging - L7, verdict, enrichment and geo the first document carried and the second doesn't repeat are gone with no trace. Zero on every deployment where each conversation gets one conn document, which is the common case; a non-zero rate means Zenarmor is re-reporting a connection and only the latest report survives.--exporter.disable-flow
opnsense_flow_correlator_fragment_disagreement_totalCounter---A NetFlow fragment whose interface, direction, VLAN or enrichment disagreed with the entry's first fragment OF ITS OWN ORIENTATION (#590, #605). The disagreeing fragment's bytes still count toward the emitted total; only its DIMENSIONS are dropped, silently until this counter. Two exclusions, both because the field differs by design rather than in error: TCPFlags is unioned across fragments (#585), and the conversation's reverse half mirrors interface and direction by construction - that case is counted by correlator_fragment_mirrored_total instead.--exporter.disable-flow
opnsense_flow_correlator_fragment_mirrored_totalCounter---A NetFlow fragment belonging to the conversation's OTHER direction - the reverse half, which shares a correlator key by design because the community id is direction-independent. This is the expected case for any bidirectional flow, not an anomaly, and it is counted rather than merely excluded so the exclusion stays visible: a collapse to zero would mean the two halves stopped sharing a key, which would break correlation itself. Before #605 these were counted as disagreements and were 48.6% of every fragment that counter could examine.--exporter.disable-flow
opnsense_flow_logs_emitted_totalCounter---Flow records shipped to the OTLP log pipeline. Zero when --flow.log-mode=off even though the correlator still runs and its metrics still move.--exporter.disable-flow
opnsense_flow_logs_truncated_totalCounter---Flow log records dropped by the --flow.max-logs-per-window budget. Truncated, never sampled, and counted: a flood on the unauthenticated NetFlow ingress is visible here rather than as a silently thinned stream. Metrics are never truncated.--exporter.disable-flow
opnsense_flow_logs_dropped_totalCounter---Flow log records dropped because the log pipeline was not accepting records - before it started or after shutdown began. Distinct from a budget truncation.--exporter.disable-flow
opnsense_flow_netflow_datagrams_totalCounterresultNetFlow datagrams by outcome. result=\"accepted\" passed the peer allowlist; \"peer_rejected\" came from outside --flow.netflow.allowed-peers; \"queue_dropped\" arrived faster than the decoders drained them (the read loop never blocks, because blocking makes the KERNEL drop datagrams where nothing can count them); \"read_error\" is a socket error. Decode outcomes are counted separately below and are a subset of \"accepted\".--exporter.disable-flow
opnsense_flow_netflow_bytes_received_totalCounter---Bytes received on the NetFlow socket, before decoding. This is wire volume of the export itself, NOT the traffic it describes - opnsense_flow_bytes_total is that.--exporter.disable-flow
opnsense_flow_netflow_records_decoded_totalCounter---Flow records successfully decoded out of NetFlow datagrams. The head of the funnel: decoded = emitted + dropped, with records lost before decoding counted as opnsense_flow_netflow_records_dropped_total{reason=\"no_template\"}.--exporter.disable-flow
opnsense_flow_netflow_records_emitted_totalCounter---Decoded records that survived repair and reached the rollup. The tail of the funnel: compare against decoded_total to see what the repair stage removed.--exporter.disable-flow
opnsense_flow_netflow_records_dropped_totalCounterreasonRecords the NetFlow lane discarded, by reason. \"no_template\" is a data flowset arriving before the template describing it - normal for up to ~2 minutes after either end restarts, since ng_netflow resends templates about every 2 minutes, and a sustained rate means template datagrams are being lost. \"vlan_duplicate\" is the parent-interface copy of a VLAN flow, which ng_netflow captures twice (~4% of bytes on the reference box) and which would otherwise be counted twice AND attributed to the parent. \"no_address\" is a record with unusable endpoints.--exporter.disable-flow
opnsense_flow_netflow_records_unmapped_totalCounter---Records whose ifIndexes resolved to NO interface, so they carry an empty interface label. They are still emitted and still counted in the volume totals - this is not a drop, and it is deliberately not a reason on records_dropped_total. Distinct from opnsense_flow_ifindex_unmapped_total, which counts failed LOOKUPS against a map that exists: this counts RECORDS, and it is the only counter that fires at all while the map is still nil, which is the cold-start window between the receiver starting and the first interface fetch landing. A burst right after a restart is that window and is expected to stop; a sustained rate means the enumeration shifted and --flow.netflow.ifindex-map needs setting. Before #365 this window was completely silent, which put gigabytes into the empty-label bucket with every health metric reading clean.--exporter.disable-flow
opnsense_flow_netflow_templates_totalCounterresultNetFlow v9 template events. \"learned\" is a template id seen for the first time; \"replaced\" is a known id re-sent with a DIFFERENT field shape, which invalidates the decoder's understanding of every record behind it. A steady replaced rate means the exporter is flapping between configurations. \"evicted\" is a template dropped because the cache was already at its budget when a new (exporter, source id, template id) arrived: the listener is unauthenticated, so an admitted sender could otherwise grow the cache without bound (#564). Eviction is least-recently-used, and a refresh of an existing key never evicts — so a non-zero rate here means either genuinely more observation domains than the budget allows, or a sender minting novel ids.--exporter.disable-flow
opnsense_flow_netflow_unexpected_field_totalCounterfieldRecords carrying a field the decoder asserts is always empty on this export. field=\"out_bytes\": OUT_BYTES/OUT_PKTS are declared in the template but were zero across all 84,513 records of the reference capture, so they are ignored rather than added to the volume. field=\"src_as\"/\"dst_as\" (#586): ng_netflow hardcodes SRC_AS/DST_AS to zero on every export path under an explicit source comment, so the fields were deleted from the record entirely rather than stored - a better ASN already ships via the GeoIP asn label. A non-zero rate on ANY of these means that assumption has expired and the decoder needs revisiting - it does NOT mean volume is currently wrong.--exporter.disable-flow
opnsense_flow_netflow_unidentified_totalCounterkindThings in the export this decoder could not interpret and stepped over, by kind. kind=\"unknown_field\" is a template element the decoder does not model, counted once per element when a template shape is first learned or CHANGED (never on the ~2-minutely re-send). Every element OPNsense actually sends is modelled, so this reads ZERO on a healthy box and any non-zero value means the export gained something new. It was expected-non-zero until #630, when the four elements the templates had always carried (TOS, both masks, next hop) were modelled rather than stepped over - a permanently non-zero counter and its WARN could never be acted on. kind=\"options_template\" and kind=\"unknown_flowset\" are control flowsets stepped over by length. Stepping over is the correct parse behaviour - doing it silently was not, and --flow.netflow.debug-capture=unidentified keeps the datagrams. The element and flowset IDs themselves are deliberately NOT labels: this arrives on an unauthenticated socket, so they go to the log line and the capture file instead.--exporter.disable-flow
opnsense_flow_egress_corrected_totalCounter---Flow records whose egress interface was corrected from the WAN the FIB lookup named to the WAN the traffic actually left by. ng_netflow derives OUTPUT_SNMP from a route lookup, but OPNsense multi-WAN policy routing happens in pf, which ng_netflow never sees: on the reference capture this mislabelled 3.36 GB of WAN2 traffic as WAN1, a 99% under-report of WAN2. A zero rate on a single-WAN box is expected; a zero rate on a policy-routed multi-WAN box means the correction is not firing and per-WAN volume is wrong.--exporter.disable-flow
opnsense_flow_policy_route_corrected_totalCounter---Flow records whose egress interface was replaced by the device pf's own state table says the traffic left by. This is the PRE-NAT copy of a policy-routed flow - the only copy that can correlate with Zenarmor - which inherits ng_netflow's OUTPUT_SNMP and therefore names the default-route WAN whatever pf did. It is distinct from egress_corrected_total, which resolves the POST-NAT copy from its source address and cannot see this case at all. A zero rate on a single-WAN box is expected; a zero rate on a policy-routed multi-WAN box means correlated per-WAN volume is still misattributed.--exporter.disable-flow
opnsense_flow_policy_route_refused_totalCounterreason, interfacePre-NAT WAN-egress records the policy-route repair REFUSED to correct, by reason. reason=\"no_state\" is the mechanism's genuine miss window: the flow ended and its pf state expired before the NetFlow record arrived (short flows; the reference box runs inactiveTimeout=15). NO poll interval closes it, and the traffic is left exactly as ng_netflow reported it rather than guessed. reason=\"unresolved_device\" means pf named an egress device the interface enumeration does not know - the fix is the enumeration, and labelling the record with the raw kernel name would split one interface across two series. THE interface LABEL IS WHERE THE BYTES ARE CURRENTLY ATTRIBUTED, NOT WHERE THEY WENT. A refused record has no known real egress - that is what made it a refusal - so this names the interface ng_netflow reported. Read it as a distribution, not as a per-WAN error count: refusals piling up on the DEFAULT-ROUTE WAN are consistent with policy-routed traffic hiding inside it and mean more of this is worth recovering, while refusals spread evenly across every WAN mean the remainder is structural (records that were never a pre-NAT egress copy) and the mechanism has nothing left to find.--exporter.disable-flow
opnsense_flow_policy_route_skipped_totalCounterreasonRecords the policy-route repair declined to act on WITHOUT it being a miss, by reason. These are NOT failures - they are the four ways the repair correctly has nothing to do - but before they were counted, three of them moved no counter at all, so a box where the repair was never running produced the same telemetry as one where it had nothing to correct. reason=\"not_wan_egress\" is the record not leaving by a WAN. It is the MAJORITY of records on any box and healthy - but it is also exactly what a wrong interface map looks like, because if the map stops reporting a device as a WAN then every record on it lands here and the repair silently ceases to exist. Watch it as a SHARE of decoded records, not as a rate: a step change in that share with no configuration change is the alarm. reason=\"post_nat\" is the copy repair 2 already resolved from its source address. reason=\"fib_agreed\" is a pf state carrying no route-to, i.e. the FIB decided and OUTPUT_SNMP was already right. reason=\"already_on_wan\" is a policy-routed state whose device ng_netflow had ALSO named, so the correction would be a no-op; it is kept apart from fib_agreed because a high fib_agreed means the box barely policy-routes while a high already_on_wan means it does and the exporter is agreeing with it.--exporter.disable-flow
opnsense_flow_pf_state_entriesGaugekindPre-NAT pf states the policy-route repair can resolve against, by kind. kind=\"total\" is every direction=\"in\" state that could be keyed; kind=\"policy_routed\" is the subset carrying a route-to, i.e. the ones that can actually change a record's egress. kind=\"skipped\" is rows that could not be keyed at all (an unmodelled protocol, an unparseable address or port) and kind=\"conflict\" is keys that were already taken - measured zero on the reference box, so a non-zero value means the tuple stopped being unique upstream and every answer from this table wants re-checking. kind=\"carried\" is the subset of \"total\" answered from an EARLIER snapshot rather than the current one - states that have expired but are still inside the retention window, which is how a flow whose state died before its NetFlow record arrived still gets corrected. Persistently zero means that rolling union is contributing nothing.--exporter.disable-flow
opnsense_flow_nat_pair_deduped_totalCounteroutcomeNAT'd conversations ng_netflow exported TWICE - pre-NAT where the flow entered, post-NAT where it left - resolved against pf's own nat_addr/nat_port mapping, by outcome. outcome=\"suppressed\" is the POST-NAT copy discarded because the pre-NAT copy of the same conversation was already emitted: without it those bytes are counted twice on the WAN, which read +38.5% against the kernel's interface counter on the reference box. Only the post-NAT copy is ever suppressed, because the pre-NAT copy carries the LAN host's own 5-tuple and is the only one that can correlate with Zenarmor. outcome=\"late_pre_nat\" is the residual: the pre-NAT copy arrived AFTER its twin had already been shipped, so the double count could not be prevented without discarding the correlatable copy. Measured at 10.8% of pairs; it should stay a small fraction of \"suppressed\" and the two move together. outcome=\"suppressed_by_conversation\" is the post-NAT copy discarded on the WEAKER proof: its exact twin was not in the identity table, but a pre-NAT record for the same conversation was seen recently, so the LAN side is demonstrably captured and these bytes reach the rollup anyway. It carries the whole mechanism on a QUIET WAN, where the WAN-side ng_netflow node batches its exports minutes behind the LAN node's and the exact window can never close - measured at 266s and 591s against a 120s window, which is how the reference box came to read 1.79x the kernel's counter for three days. outcome=\"unpaired\" is the GUARD: a post-NAT copy pf calls a translation that was emitted anyway because neither proof was available. Read unpaired / (unpaired + suppressed + suppressed_by_conversation) - it is the share of proven duplicates the stage failed to remove, and it goes to 1 when the mechanism breaks instead of everything simply reading zero. A small non-zero value is normal, because a post-NAT copy that legitimately arrives first is unpaired at that instant and is counted again as \"late_pre_nat\" when its twin lands. ALL ARE ZERO ON A BOX WITH NO CAPTURED ETHERNET WAN - including one whose WAN is PPPoE, which exports nothing at all - and that is correct, not a fault.--exporter.disable-flow
opnsense_flow_nat_pair_entriesGaugekindThe NAT-pair de-duplication's two tables, by kind. kind=\"translations\" is how many post-NAT tuples pf's index can canonicalise - BOTH directional forms of each translated state, so roughly twice the number of translated states; kind=\"carried\" is the subset answered from an EARLIER snapshot, the same rolling union the policy-route repair uses. kind=\"identities\" is the live size of the emitted-record identity table a post-NAT copy is matched against, and kind=\"conversations\" the live size of the second, longer-lived table holding one entry per TRANSLATED conversation whose pre-NAT side has been seen - the evidence behind outcome=\"suppressed_by_conversation\". It holds one entry per conversation rather than one per export, so it scales with how much the box NATs rather than with its record rate, but its TTL is 7.5x longer and it is usually the LARGER of the two in absolute terms (4,039 against 3,026 measured). Both sit against the same bound, so watch them together. kind=\"conflict\" is a post-NAT tuple that was already mapped to a DIFFERENT conversation; a SMALL non-zero value is normal - measured 6-14 per build against ~7,000 entries on the reference box, about 0.15% - because a translation can genuinely be reused inside the retention window, and it fails safe: the conflicted tuple resolves to the wrong conversation, whose twin then will not match on volume, so the record is emitted rather than suppressed. A LARGE value means the index is mostly guessing. kind=\"skipped\" is translated rows that could not be keyed at all.--exporter.disable-flow
opnsense_flow_pf_state_age_secondsGauge---Age of the pf state snapshot the policy-route repair resolves against. It is rebuilt every minute (the full table is ~3 MB and ~650 ms on the reference box, so this is not a free request), and a value rising past a few multiples of that means the fetch is failing and corrections are being made against a stale routing picture.--exporter.disable-flow
opnsense_flow_dedupe_entriesGauge---Flow instances currently held in the VLAN de-duplication table.--exporter.disable-flow
opnsense_flow_vlan_child_preferred_totalCounter---VLAN duplicates resolved in favour of the VLAN CHILD copy after the trunk copy had already been held. ng_netflow exports the trunk hook's flows and the child hook's flows in separate datagrams, trunk first, so without this the surviving copy attributes every VLAN's traffic to the trunk interface. A zero rate on a box with VLAN interfaces means the attribution fix is not firing and per-VLAN volume is collapsed onto the trunk.--exporter.disable-flow
opnsense_flow_vlan_subnet_attributed_totalCounter---Flow records moved from a TRUNK interface onto the VLAN child whose configured subnet owns the address, resolved on first sight instead of by waiting to see which copy the exporter flushed first. The 2-second hold that preceded this covers only 70.8% of real trunk/child pairs (measured p50 gap 954 ms but p95 5.7 s and p99 31.2 s), and for the other 29.2% the trunk copy had already been emitted, so every one of those flows was attributed to the trunk. This also attributes records that have NO second copy at all - 247,105 of them in an 18h35m measurement - which no hold window of any size could reach. A zero rate on a box with VLAN interfaces that carry configured subnets means the attribution is not firing and per-VLAN volume is collapsing onto the trunk.--exporter.disable-flow
opnsense_flow_vlan_late_child_copies_totalCounter---VLAN duplicates that arrived attributing the flow BETTER than the copy already emitted, and therefore too late to correct it. This is the residual the repair stage cannot fix: an emitted record has been counted and shipped, so it cannot be taken back, and emitting the better copy as well would double-count real bytes. It is counted rather than left silent because it is the exact measure of remaining misattribution - it was 29.2% of pairs before subnet attribution existed and should sit near zero now, moving only for addresses that match no VLAN child subnet or several of them. A sustained non-zero rate means subnet evidence is missing for a VLAN that needs it: check that the interface has a configured subnet and that no two children overlap.--exporter.disable-flow
opnsense_flow_repair_held_recordsGauge---Flow records parked in the repair stage waiting to see whether a copy on a VLAN child beats them. They are neither emitted nor dropped yet, so this is the term that closes records_in = emitted + dropped + no_address + held. A value that grows without bound means records are not being released.--exporter.disable-flow
opnsense_flow_dedupe_entries_dropped_totalCounterreasonEntries removed from the de-duplication table. reason=\"ttl\" is the healthy path - the instance aged out having done its job. reason=\"capacity\" means the table was full and an entry was evicted early, so a duplicate arriving afterwards is NO LONGER SUPPRESSED and reaches the rollup twice; a non-zero rate is the signal to raise the bound. reason=\"nat_capacity\" and reason=\"nat_conversation_capacity\" are the same signal for the NAT-pair stage's two tables, and read the same way - each dropped entry is a NAT'd conversation that will be counted twice on the WAN.--exporter.disable-flow
opnsense_flow_interface_infoGaugedevice, interface, ifindexThe resolved NetFlow ifIndex map as an info metric, one series per index, always 1 - the data is in the labels. It exists to make two label spaces joinable: the box's own per-hook counters (opnsense_netflow_cache_*) are keyed by kernel DEVICE, while every flow and capture metric is keyed by the configured DESCRIPTION, and the single most valuable NetFlow health statement spans both - \"this interface is configured for capture and its OWN ng_netflow node has been frozen at zero\". Join through it with group_left: a dead hook is otherwise a two-panel eyeball correlation, because a fresh record age proves only that the interface was NAMED (ng_netflow fills the far side of each flow from a FIB lookup), never that its own hook is alive. ifindex is a POSITION in the box's ifinfo output, so it renumbers when any interface is added or removed - treat a changed value as the map having moved, not as a relabel. device is empty for index 0, which is traffic the firewall itself originated; interface is empty for a port with no OPNsense assignment, which still holds a slot in the enumeration.--exporter.disable-flow
opnsense_flow_interface_capture_unsupportedGaugedevice, interface, reasonInterfaces whose kernel device can NEVER capture NetFlow, whatever the box's capture configuration says. Present (and always 1) only for such a device; ABSENT means capable, so this is an exception marker rather than a per-interface flag. reason=\"pppoe_framing_node\" is the only value today: ng_netflow attaches to mpd's framing node rather than the ng_iface node ng_pppoe exposes, so ngctl mkpeer on a PPPoE interface SUCCEEDS, creates the node, and then counts zero packets forever (#368). No configuration clears it, which is why this suppresses OPNsenseNetFlowHookDead rather than raising an alert of its own. Nothing is lost when it appears: ng_netflow fills the far side of every flow from a FIB lookup, so the WAN's traffic is still captured through the other interfaces' hooks and still attributed to it - 2.09 GB in 45m on the reference box while this very device's hook read zero. Untick the interface in Reporting/NetFlow to stop asking for a capture that cannot happen; leaving it selected costs only a dead netgraph node.--exporter.disable-flow
opnsense_flow_ifindex_entriesGauge---Entries in the NetFlow ifIndex-to-interface map, including the synthetic index 0 (traffic originated by the firewall itself).--exporter.disable-flow
opnsense_flow_ifindex_conflictsGaugereasonOperator overrides from --flow.netflow.ifindex-map that DISAGREE with the map derived from the API. reason=\"derived_differs\" counts pins where the derivation produced a DIFFERENT interface at that index; reason=\"derived_absent\" counts pins at an index the derivation produced nothing for at all, which is normal for an index beyond the enumeration and says nothing about the ordering. Non-zero does NOT say which side is right: a pin is a static assertion, and adding or removing ANY interface renumbers every position above it, so a pin that was correct when written goes stale on its own - that is what #516 turned out to be, a new VLAN shifting three pins by one and relabelling the WAN as the tailnet. Settle it against the box, not this counter: ifinfo order, or decisively the ifaceN hook names on its ng_netflow nodes (ngctl show netflow_<device>:). The override wins either way, so a stale pin is actively mislabelling while a correct one leaves every unpinned index suspect.--exporter.disable-flow
opnsense_flow_ifindex_map_age_secondsGauge---Age of the ifIndex map. ng_netflow's indices are positional over ifinfo output, so adding or removing ANY interface renumbers everything and silently remaps historical series. A map that stops being refreshed is therefore a correctness problem, not a staleness nuisance.--exporter.disable-flow
opnsense_flow_ifindex_source_disagreementsGaugereasonCross-checks that failed on the derived ifIndex enumeration. reason=\"stated_index\" counts devices where the ifIndex the API states differs from the position the enumeration put them at, which means an interface was removed and every index above it shifted down. reason=\"unlisted_device\" counts interfaces the box reports that the enumeration does not contain at all, so they can never be resolved from an ifIndex. Either one non-zero means the labels on every NetFlow series are suspect - the derivation was measurably wrong this way for months (#361).--exporter.disable-flow
opnsense_flow_ifindex_unmapped_totalCounter---ifIndex lookups that resolved to no interface. These records are still counted, with an EMPTY interface label - a wrong interface name is worse than a missing one. A rising rate after a network change means the enumeration shifted and --flow.netflow.ifindex-map needs setting.--exporter.disable-flow

Gateways

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_gateways_infoGaugename, description, device, protocol, enabled, weight, interface, upstreamInformation of the gateway--exporter.disable-gateways
opnsense_gateways_monitor_infoGaugename, enabled, no_route, addressGateway monitoring configuration--exporter.disable-gateways
opnsense_gateways_rtt_millisecondsGaugename, addressRTT is the average (mean) of the round trip time in milliseconds by name and address--exporter.disable-gateways
opnsense_gateways_rttd_millisecondsGaugename, addressRTTd is the standard deviation of the round trip time in milliseconds by name and address--exporter.disable-gateways
opnsense_gateways_rtt_low_millisecondsGaugename, addressGateway low latency threshold--exporter.disable-gateways
opnsense_gateways_rtt_high_millisecondsGaugename, addressGateway high latency threshold--exporter.disable-gateways
opnsense_gateways_loss_percentageGaugename, addressThe current gateway loss percentage by name and address--exporter.disable-gateways
opnsense_gateways_loss_low_percentageGaugename, addressGateway low packet loss threshold--exporter.disable-gateways
opnsense_gateways_loss_high_percentageGaugename, addressGateway high packet loss threshold--exporter.disable-gateways
opnsense_gateways_probe_interval_secondsGaugename, addressGateway probe interval--exporter.disable-gateways
opnsense_gateways_probe_period_secondsGaugename, addressGateway probe period--exporter.disable-gateways
opnsense_gateways_probe_timeout_secondsGaugename, addressGateway probe timeout--exporter.disable-gateways
opnsense_gateways_statusGaugename, address, default_gatewayStatus of the gateway by name and address (0 = Offline, 1 = Online, 2 = Unknown, 3 = Pending, 4 = Packetloss, 5 = Latency, 6 = Offline (forced))--exporter.disable-gateways
opnsense_gateways_force_downGaugename, address1 if the gateway is administratively forced down, 0 otherwise--exporter.disable-gateways
opnsense_gateways_virtualGaugename, address1 if the gateway is virtual, 0 otherwise--exporter.disable-gateways
opnsense_gateways_dynamicGaugename, address1 if the gateway is dynamically configured, 0 otherwise--exporter.disable-gateways
opnsense_gateways_priorityGaugename, addressGateway priority (lower value = higher priority)--exporter.disable-gateways
opnsense_gateways_monitor_killstatesGaugename, address1 if pf states are killed for this gateway when its monitor marks it down, 0 otherwise. Determines what a failover actually does to already-established connections through this gateway.--exporter.disable-gateways
opnsense_gateways_monitor_killstates_priorityGaugename, address1 if this gateway's state-killing on down is priority-scoped, 0 otherwise. Sibling configuration to monitor_killstates.--exporter.disable-gateways

HA Sync Status

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_hasync_remote_reachableGauge---Whether the HA sync remote peer is reachable (1 = reachable, 0 = unreachable/unconfigured)--exporter.enable-hasync
opnsense_hasync_remote_version_matchGauge---Whether the remote peer firmware version matches the local version (1 = match, 0 = mismatch)--exporter.enable-hasync
opnsense_hasync_remote_version_infoGaugeremote_version, local_versionHA sync firmware version information (value is always 1; see labels)--exporter.enable-hasync
opnsense_hasync_remote_services_totalGauge---Total number of services in the cached remote HA peer service list--exporter.enable-hasync
opnsense_hasync_remote_service_runningGaugeservice, idWhether a service is running on the remote HA peer (1 = running, 0 = stopped)--exporter.enable-hasync

HAProxy

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_haproxy_service_runningGauge---Whether the HAProxy service is running (1 = running, 0 = stopped/disabled)--exporter.disable-haproxy
opnsense_haproxy_process_uptime_secondsGauge---HAProxy process uptime in seconds--exporter.disable-haproxy
opnsense_haproxy_process_current_connectionsGauge---Current number of connections on the HAProxy process--exporter.disable-haproxy
opnsense_haproxy_process_connections_totalCounter---Cumulative connections accepted by the HAProxy process--exporter.disable-haproxy
opnsense_haproxy_process_requests_totalCounter---Cumulative HTTP requests processed by the HAProxy process--exporter.disable-haproxy
opnsense_haproxy_process_idle_time_percentGauge---HAProxy process idle time percentage--exporter.disable-haproxy
opnsense_haproxy_frontend_statusGaugefrontendHAProxy frontend status (1 = OPEN, 0 = otherwise)--exporter.disable-haproxy
opnsense_haproxy_frontend_current_sessionsGaugefrontendCurrent sessions on this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_sessions_totalCounterfrontendCumulative sessions on this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_bytes_in_totalCounterfrontendCumulative bytes received by this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_bytes_out_totalCounterfrontendCumulative bytes sent by this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_request_errors_totalCounterfrontendCumulative request errors on this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_requests_denied_totalCounterfrontendCumulative requests denied by security rules on this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_http_responses_totalCounterfrontend, codeCumulative HTTP responses by status code class on this frontend--exporter.disable-haproxy
opnsense_haproxy_backend_statusGaugebackendHAProxy backend status (1 = UP, 0 = otherwise)--exporter.disable-haproxy
opnsense_haproxy_backend_current_sessionsGaugebackendCurrent sessions on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_sessions_totalCounterbackendCumulative sessions on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_bytes_in_totalCounterbackendCumulative bytes received by this backend--exporter.disable-haproxy
opnsense_haproxy_backend_bytes_out_totalCounterbackendCumulative bytes sent by this backend--exporter.disable-haproxy
opnsense_haproxy_backend_queue_currentGaugebackendCurrent requests queued on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_connection_errors_totalCounterbackendCumulative connection errors on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_response_errors_totalCounterbackendCumulative response errors on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_retries_totalCounterbackendCumulative connection retries on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_redispatches_totalCounterbackendCumulative request redispatches on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_active_serversGaugebackendNumber of active servers on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_backup_serversGaugebackendNumber of backup servers on this backend--exporter.disable-haproxy
opnsense_haproxy_backend_http_responses_totalCounterbackend, codeCumulative HTTP responses by status code class on this backend--exporter.disable-haproxy
opnsense_haproxy_server_statusGaugebackend, serverHAProxy server status (1 = UP, 0 = otherwise)--exporter.disable-haproxy
opnsense_haproxy_server_current_sessionsGaugebackend, serverCurrent sessions on this server--exporter.disable-haproxy
opnsense_haproxy_server_sessions_totalCounterbackend, serverCumulative sessions on this server--exporter.disable-haproxy
opnsense_haproxy_server_bytes_in_totalCounterbackend, serverCumulative bytes received by this server--exporter.disable-haproxy
opnsense_haproxy_server_bytes_out_totalCounterbackend, serverCumulative bytes sent by this server--exporter.disable-haproxy
opnsense_haproxy_server_queue_currentGaugebackend, serverCurrent requests queued on this server--exporter.disable-haproxy
opnsense_haproxy_server_connection_errors_totalCounterbackend, serverCumulative connection errors on this server--exporter.disable-haproxy
opnsense_haproxy_server_response_errors_totalCounterbackend, serverCumulative response errors on this server--exporter.disable-haproxy
opnsense_haproxy_server_check_failures_totalCounterbackend, serverCumulative failed health checks on this server--exporter.disable-haproxy
opnsense_haproxy_server_downtime_seconds_totalCounterbackend, serverCumulative downtime of this server in seconds--exporter.disable-haproxy
opnsense_haproxy_server_weightGaugebackend, serverCurrent effective weight of this server--exporter.disable-haproxy
opnsense_haproxy_connection_limitGauge---HAProxy process-wide connection limit (Maxconn)--exporter.disable-haproxy
opnsense_haproxy_ssl_current_connectionsGauge---Current SSL/TLS connections on the HAProxy process--exporter.disable-haproxy
opnsense_haproxy_frontend_requests_totalCounterfrontendCumulative HTTP requests processed by this frontend--exporter.disable-haproxy
opnsense_haproxy_frontend_session_limitGaugefrontendConfigured session limit on this frontend--exporter.disable-haproxy
opnsense_haproxy_backend_queue_time_avg_secondsGaugebackendAverage time spent in queue on this backend, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_backend_connect_time_avg_secondsGaugebackendAverage time to connect to a server on this backend, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_backend_response_time_avg_secondsGaugebackendAverage server response time on this backend, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_backend_total_time_avg_secondsGaugebackendAverage total request time on this backend, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_backend_selected_totalCounterbackendCumulative number of times this backend was selected by the load balancer (lbtot)--exporter.disable-haproxy
opnsense_haproxy_backend_aborts_totalCounterbackend, sideCumulative aborted requests on this backend, by side--exporter.disable-haproxy
opnsense_haproxy_server_queue_time_avg_secondsGaugebackend, serverAverage time spent in queue on this server, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_server_connect_time_avg_secondsGaugebackend, serverAverage time to connect to this server, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_server_response_time_avg_secondsGaugebackend, serverAverage response time from this server, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_server_total_time_avg_secondsGaugebackend, serverAverage total request time on this server, over the last 1024 requests--exporter.disable-haproxy
opnsense_haproxy_server_check_downs_totalCounterbackend, serverCumulative number of UP->DOWN health-check transitions on this server--exporter.disable-haproxy
opnsense_haproxy_server_last_state_change_secondsGaugebackend, serverSeconds since this server's last health-check state change--exporter.disable-haproxy
opnsense_haproxy_stick_table_sizeGaugetable, typeConfigured maximum entry count for this stick table--exporter.disable-haproxy
opnsense_haproxy_stick_table_usedGaugetable, typeCurrent entry count in this stick table--exporter.disable-haproxy

Hardware

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_hardware_dmi_infoGaugemanufacturer, product, version, serial, family, bios_vendor, bios_version, bios_releaseDMI system and BIOS identity as reported by dmidecode (value is always 1; use labels). Silent when the os-dmidecode plugin is absent.--exporter.disable-hardware
opnsense_hardware_psu_statusGaugepsuDeciso DEC-series power supply status (1 = powered, 0 = not powered). Only emitted on hardware with a GPIO power-status device; silent when the os-dec-hw plugin is absent or no such hardware is detected.--exporter.disable-hardware

Host Discovery

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_hostdiscovery_hostsGaugeinterface, source, manufacturerNumber of hosts in the discovered-host inventory, by interface, source and manufacturer. source is \"discovery\" when the hostwatch daemon is enabled (persistent inventory, survives reboots and cache expiry) or \"arp-ndp\" when it is disabled (a live fallback that duplicates the arp_table/ndp collectors). manufacturer is the OUI vendor lookup of the MAC (organization_name), same label name/convention as arp_table/ndp's manufacturer label; \"unknown\" when the box could not resolve an OUI (e.g. a randomized-MAC device). Aggregate only: never a per-host series.--exporter.disable-hostdiscovery
opnsense_hostdiscovery_hosts_recentGaugeinterface, source, manufacturerNumber of hosts in the discovered-host inventory whose last_seen falls within a 15 minute window, by interface, source and manufacturer. Always 0 for source=\"arp-ndp\" rows, which carry no last_seen timestamp to judge recency from.--exporter.disable-hostdiscovery

IDS/IPS (Suricata)

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_ids_statusGauge---Suricata IDS/IPS service status (1 = running, 0 = stopped, -1 = disabled/unconfigured)--exporter.disable-ids
opnsense_ids_ips_mode_enabledGauge---Whether Suricata runs inline as an IPS that drops traffic (1) rather than a passive IDS (0)--exporter.disable-ids
opnsense_ids_promiscuous_mode_enabledGauge---Whether the IDS monitoring interface is in promiscuous mode (1 = yes, 0 = no)--exporter.disable-ids
opnsense_ids_alert_log_filesGauge---Number of Suricata eve log files on disk (the live eve.json plus rotated copies)--exporter.disable-ids
opnsense_ids_alert_log_size_bytesGaugefilenameSize in bytes of each Suricata eve log file--exporter.disable-ids
opnsense_ids_recent_alertsGaugeactionSuricata alerts observed within the lookback window (--exporter.ids-alert-lookback), by action. A GAUGE, not a counter (query_alerts reads a windowed, saturating backend); a floor when more than 500 alerts fall inside the window. Requires --exporter.enable-ids-alerts.--exporter.disable-ids
opnsense_ids_ruleset_enabledGaugerulesetWhether an installable Suricata ruleset is enabled (1) or disabled (0), by ruleset filename--exporter.disable-ids
opnsense_ids_ruleset_last_updated_timestamp_secondsGaugerulesetUnix timestamp of the last download of a ruleset (best-effort from the box-local modified time; omitted when never downloaded)--exporter.disable-ids
opnsense_ids_installed_rules_totalGauge---Total number of installed Suricata rules in the rule cache (a current count; treat as RAW, not rate)--exporter.disable-ids

IPsec

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_ipsec_phase1_statusGaugedescription, nameIPsec phase1 (1 = connected, 0 = down)--exporter.disable-ipsec
opnsense_ipsec_phase1_install_timeGaugedescription, nameIPsec phase1 install time--exporter.disable-ipsec
opnsense_ipsec_phase1_bytes_in_totalCounterdescription, nameIPsec phase1 bytes in--exporter.disable-ipsec
opnsense_ipsec_phase1_bytes_out_totalCounterdescription, nameIPsec phase1 bytes out--exporter.disable-ipsec
opnsense_ipsec_phase1_packets_in_totalCounterdescription, nameIPsec phase1 packets in--exporter.disable-ipsec
opnsense_ipsec_phase1_packets_out_totalCounterdescription, nameIPsec phase1 packets out--exporter.disable-ipsec
opnsense_ipsec_phase2_install_timeGaugedescription, name, phase1_nameIPsec phase2 install time--exporter.disable-ipsec
opnsense_ipsec_phase2_bytes_in_totalCounterdescription, name, phase1_nameIPsec phase2 bytes in--exporter.disable-ipsec
opnsense_ipsec_phase2_bytes_out_totalCounterdescription, name, phase1_nameIPsec phase2 bytes out--exporter.disable-ipsec
opnsense_ipsec_phase2_packets_in_totalCounterdescription, name, phase1_nameIPsec phase2 packets in--exporter.disable-ipsec
opnsense_ipsec_phase2_packets_out_totalCounterdescription, name, phase1_nameIPsec phase2 packets out--exporter.disable-ipsec
opnsense_ipsec_phase2_rekey_timeGaugedescription, name, phase1_nameIPsec phase2 rekey time--exporter.disable-ipsec
opnsense_ipsec_phase2_life_timeGaugedescription, name, phase1_nameIPsec phase2 life time--exporter.disable-ipsec
opnsense_ipsec_phase2_establishedGaugedescription, name, phase1_nameWhether the IPsec phase2 (child SA) is fully installed and passing traffic (1 = INSTALLED, 0 = any other transitional/rekeying/deleting state). Phase1 being connected does not guarantee every child SA is up; check this alongside opnsense_ipsec_phase1_status to catch a tunnel where the parent IKE SA is fine but one traffic selector's child SA has failed or is stuck rekeying.--exporter.disable-ipsec
opnsense_ipsec_service_runningGauge---Whether the service is running (1 = running, 0 = stopped/disabled)--exporter.disable-ipsec
opnsense_ipsec_pool_leases_onlineGaugepool, netNumber of online leases in the IPsec mode-cfg pool--exporter.disable-ipsec
opnsense_ipsec_pool_leases_offlineGaugepool, netNumber of offline leases in the IPsec mode-cfg pool--exporter.disable-ipsec
opnsense_ipsec_pool_sizeGaugepool, netTotal size (address capacity) of the IPsec mode-cfg pool--exporter.disable-ipsec
opnsense_ipsec_sad_entriesGaugesatype, ikeid, phase1descNumber of installed kernel IPsec security associations (setkey -D), grouped by satype--exporter.disable-ipsec
opnsense_ipsec_sa_age_secondsGaugeikeid, phase2desc, reqidAge in seconds of the oldest installed kernel SA in each reqid (child-SA) group--exporter.disable-ipsec
opnsense_ipsec_sa_lifetime_hard_secondsGaugeikeid, phase2desc, reqidSoonest hard-expiry lifetime in seconds across the kernel SAs in each reqid group--exporter.disable-ipsec
opnsense_ipsec_sa_lifetime_soft_secondsGaugeikeid, phase2desc, reqidSoonest soft-expiry (rekey) lifetime in seconds across the kernel SAs in each reqid group--exporter.disable-ipsec
opnsense_ipsec_sa_bytes_current_totalCounterikeid, phase2desc, reqidCumulative bytes processed by the most-utilized installed kernel SA in each reqid (child-SA) group (the higher of the two during a brief rekey overlap). Resets to a small value on every rekey (a new SPI starts counting from zero) — compare against sa_bytes_soft_limit to see how close the group is to its next byte-triggered rekey.--exporter.disable-ipsec
opnsense_ipsec_sa_bytes_soft_limitGaugeikeid, phase2desc, reqidConfigured soft (rekey-triggering) byte-count limit for the kernel SAs in each reqid group. Only present when the box configures a byte-count lifetime for this child SA; a limit of 0 means unlimited and is not exported as a fabricated zero.--exporter.disable-ipsec
opnsense_ipsec_sa_bytes_hard_limitGaugeikeid, phase2desc, reqidConfigured hard (forced-expiry) byte-count limit for the kernel SAs in each reqid group. Only present when the box configures a byte-count lifetime for this child SA; a limit of 0 means unlimited and is not exported as a fabricated zero.--exporter.disable-ipsec
opnsense_ipsec_sa_allocated_current_totalCounterikeid, phase2desc, reqidCumulative packets (\"allocations\") processed by the most-utilized installed kernel SA in each reqid (child-SA) group (the higher of the two during a brief rekey overlap). The packet-count equivalent of sa_bytes_current_total; some child SAs are configured with a packet-count rekey margin instead of, or alongside, a byte-count one.--exporter.disable-ipsec
opnsense_ipsec_sa_allocated_soft_limitGaugeikeid, phase2desc, reqidConfigured soft (rekey-triggering) packet-count limit for the kernel SAs in each reqid group. Only present when the box configures a packet-count lifetime for this child SA; a limit of 0 means unlimited and is not exported as a fabricated zero.--exporter.disable-ipsec
opnsense_ipsec_sa_allocated_hard_limitGaugeikeid, phase2desc, reqidConfigured hard (forced-expiry) packet-count limit for the kernel SAs in each reqid group. Only present when the box configures a packet-count lifetime for this child SA; a limit of 0 means unlimited and is not exported as a fabricated zero.--exporter.disable-ipsec
opnsense_ipsec_sad_nat_traversalGaugeikeidWhether any kernel SA for the IKE SA is NAT-traversed (1 = NAT-T detected, 0 = not)--exporter.disable-ipsec
opnsense_ipsec_spd_policiesGaugedirectionNumber of installed kernel IPsec security policies (setkey -DP), grouped by direction--exporter.disable-ipsec
opnsense_ipsec_lease_onlineGaugepool, userWhether the IPsec mode-cfg lease is currently online (1 = online, 0 = offline). Per-user detail; only emitted with --exporter.enable-ipsec-lease-details--exporter.disable-ipsec
opnsense_ipsec_config_dirtyGauge---Whether there is an uncommitted (staged but not applied) IPsec configuration change (1 = dirty, 0 = clean)--exporter.disable-ipsec
opnsense_ipsec_legacy_enabledGauge---Whether IPsec is enabled in the configuration (1 = enabled, 0 = disabled)--exporter.disable-ipsec

ISC DHCPv4

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_dhcpv4_leases_totalGauge---Total number of ISC DHCPv4 leases--exporter.disable-dhcpv4
opnsense_dhcpv4_leases_by_interfaceGaugeinterfaceNumber of ISC DHCPv4 leases per interface--exporter.disable-dhcpv4
opnsense_dhcpv4_leases_reserved_totalGauge---Total number of reserved (static) ISC DHCPv4 leases--exporter.disable-dhcpv4
opnsense_dhcpv4_leases_dynamic_totalGauge---Total number of dynamic ISC DHCPv4 leases--exporter.disable-dhcpv4
opnsense_dhcpv4_lease_infoGaugeaddress, hostname, mac, interface, type, state, status, devicePer-lease ISC DHCPv4 information (value is always 1; use labels). Only emitted when --exporter.enable-dhcpv4-details is set. device is the raw logical interface id and interface the assigned description; they diverge on VLAN children and bridges, and only device joins against the interfaces metrics (the #544 item-5 pattern).--exporter.disable-dhcpv4

ISC DHCPv6

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_dhcpv6_leases_totalGauge---Total number of ISC DHCPv6 leases--exporter.disable-dhcpv6
opnsense_dhcpv6_leases_by_interfaceGaugeinterfaceNumber of ISC DHCPv6 leases per interface--exporter.disable-dhcpv6
opnsense_dhcpv6_leases_reserved_totalGauge---Total number of reserved (static) ISC DHCPv6 leases--exporter.disable-dhcpv6
opnsense_dhcpv6_leases_dynamic_totalGauge---Total number of dynamic ISC DHCPv6 leases--exporter.disable-dhcpv6
opnsense_dhcpv6_lease_infoGaugeaddress, mac, duid, if_descr, state, status, type, devicePer-lease ISC DHCPv6 information (value is always 1; use labels). Only emitted when --exporter.enable-dhcpv6-details is set. device is the raw logical interface id and if_descr the assigned description; they diverge on VLAN children and bridges, and only device joins against the interfaces metrics (the #544 item-5 pattern).--exporter.disable-dhcpv6
opnsense_dhcpv6_pd_prefixes_totalGauge---Total number of ISC DHCPv6 prefix delegation entries--exporter.disable-dhcpv6
opnsense_dhcpv6_pd_prefixes_activeGauge---Number of active ISC DHCPv6 prefix delegation entries--exporter.disable-dhcpv6

Interfaces

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_interfaces_mtu_bytesGaugeinterface, device, typeThe MTU value of the interface--exporter.disable-interfaces
opnsense_interfaces_received_bytes_totalCounterinterface, device, typeBytes received on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_transmitted_bytes_totalCounterinterface, device, typeBytes transmitted on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_received_multicasts_totalCounterinterface, device, typeMulticasts received on this interface by interface name and device--exporter.disable-interfaces
opnsense_interfaces_transmitted_multicasts_totalCounterinterface, device, typeMulticasts transmitted on this interface by interface name and device--exporter.disable-interfaces
opnsense_interfaces_input_errors_totalCounterinterface, device, typeInput errors on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_output_errors_totalCounterinterface, device, typeOutput errors on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_collisions_totalCounterinterface, device, typeCollisions on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_received_packets_totalCounterinterface, device, typeTotal packets received on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_transmitted_packets_totalCounterinterface, device, typeTotal packets transmitted on this interface by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_send_queue_lengthGaugeinterface, device, typeCurrent send queue length on this interface by interface name and device--exporter.disable-interfaces
opnsense_interfaces_send_queue_max_lengthGaugeinterface, device, typeMaximum send queue length on this interface by interface name and device--exporter.disable-interfaces
opnsense_interfaces_send_queue_drops_totalCounterinterface, device, typeSend queue drops on this interface by interface name and device. Not emitted for an interface whose reported figure has wrapped through an unsigned 32-bit field (see input_queue_drops_total). Note this counter reads the legacy if_snd.ifq_drops and is structurally always 0 on modern buf_ring drivers, so it is not worth alerting on.--exporter.disable-interfaces
opnsense_interfaces_input_queue_drops_totalCounterinterface, device, typeInput queue drops on this interface by interface name and device. A value that reinterprets as a negative int32 has wrapped through an unsigned 32-bit field below the exporter (prod ixl1 reports 4294958080, which is -9216) and is suppressed rather than published: the series is absent for that interface, since a fabricated 0 would falsely assert it reported no drops.--exporter.disable-interfaces
opnsense_interfaces_link_stateGaugeinterface, device, typeLink state of this interface (1=up, 0=down, 2=unknown) by interface name and device. 2 (unknown) is reported by the kernel for carrier-less pseudo-devices such as PPPoE and tun/tailscale interfaces, which have no carrier-sense concept and are not actually down; alert on link_state==0, not link_state!=1.--exporter.disable-interfaces
opnsense_interfaces_line_rate_bitsGaugeinterface, device, typeLine rate in bits per second on this interface by interface name and device. Not emitted for a carrier-less pseudo-device (the kernel's link state reads unknown — PPPoE, tun/tailscale, similar overlay interfaces): the kernel reports a placeholder there rather than a real negotiated rate (ng_pppoe reports a static 64000 regardless of the underlying WAN's actual speed), and publishing it would make rate(bytes)/line_rate_bits wrong by orders of magnitude (#644). Ethernet devices are unaffected.--exporter.disable-interfaces
opnsense_interfaces_unknown_protocol_packets_totalCounterinterface, device, typePackets delivered to this interface that the network stack could not classify (kernel \"packets for unknown protocol\" counter) by interface name and device.--exporter.disable-interfaces
opnsense_interfaces_attach_or_statistics_reset_uptime_secondsGaugeinterface, device, typeSystem-uptime reading at which this interface was attached OR its statistics were explicitly reset (the kernel does not distinguish the two cases) by interface name and device. Only emitted when the box reports this marker. Compute age since attach/reset with opnsense_system_uptime_seconds - opnsense_interfaces_attach_or_statistics_reset_uptime_seconds.--exporter.disable-interfaces
opnsense_interfaces_admin_upGaugeinterface, deviceAdministrative status of this interface (1 = configured up / ifconfig UP flag set, 0 = admin down). Compare with link_state for carrier detection. Join with other interfaces metrics on the device label; the interface label here is the overview description, which can differ from the traffic-based metrics' interface name for unassigned/pseudo devices.--exporter.disable-interfaces
opnsense_interfaces_admin_enabledGaugeinterface, deviceOPNsense config-level enabled flag for this interface (1 = enabled in Interfaces > [name], 0 = administratively disabled in config). Distinct from admin_up, which reflects the ifconfig UP flag (kernel/runtime state) rather than configuration: use admin_enabled to separate a deliberately-disabled interface (expected, not an incident) from one that is enabled in config but down (a real problem). This endpoint's response is cached for --exporter.cache-ttl (#573); that is fine here since this is config data, not a live status the cache would stale-freeze.--exporter.disable-interfaces
opnsense_interfaces_infoGaugeinterface, device, identifier, media, link_type, vlan_tag, vlan_parent, physical, driver, hw_offload_capabilitiesInterface identity from the interfaces overview API (media/duplex, link type, VLAN topology) plus the kernel driver name and HW offload capability set from the traffic API. Value is always 1. Join on the device label. The media label can change on link renegotiation, starting a new series. driver is the kernel driver name (e.g. igb, ixl, ixgbe) — ixl/ixgbe/igb override IFCOUNTER_OQDROPS in their own if_get_counter, so a per-driver caveat on output_queue_drops_total and on a wrapped input_queue_drops_total figure can be checked against it. hw_offload_capabilities is the box's checksum/TSO/LRO offload state, comma-joined and sorted for stability; both are empty when the device has no matching row in the traffic API fetch.--exporter.disable-interfaces
opnsense_interfaces_address_family_received_packets_totalCounterdevice, familyPackets received on this device's addresses of this family (ipv4/ipv6).--exporter.disable-interfaces
opnsense_interfaces_address_family_received_bytes_totalCounterdevice, familyBytes received on this device's addresses of this family (ipv4/ipv6).--exporter.disable-interfaces
opnsense_interfaces_address_family_sent_packets_totalCounterdevice, familyPackets sent from this device's addresses of this family (ipv4/ipv6).--exporter.disable-interfaces
opnsense_interfaces_address_family_sent_bytes_totalCounterdevice, familyBytes sent from this device's addresses of this family (ipv4/ipv6).--exporter.disable-interfaces
opnsense_interfaces_output_queue_drops_totalCounterdevicePackets the kernel dropped on this device's output queue (netstat's Drop column, IFCOUNTER_OQDROPS). CAVEAT, and it matters: on ixl, ixgbe and igb the driver overrides IFCOUNTER_OQDROPS in its own if_get_counter and reports its own figure, so on those NICs a flat zero here does NOT prove nothing is dropping — netmap's drops in particular are invisible. Treat a non-zero value as real and a zero on those drivers as no information. Covers every device netstat reports, including unassigned ports and pseudo-devices that send_queue_drops_total omits entirely (on the dev boxes tailscale0 is the only device with non-zero drops and the only one missing from that metric).--exporter.disable-interfaces
opnsense_interfaces_lagg_infoGaugedevice, protocol, hashLAGG (link aggregation) interface protocol/hash configuration. Value is always 1. Only emitted for interfaces that are themselves a lagg device. Join on the device label.--exporter.disable-interfaces
opnsense_interfaces_lagg_active_portsGaugedeviceNumber of currently active (traffic-carrying) member ports in this LAGG interface. Only emitted when the box reports a lagg statistics block for this device.--exporter.disable-interfaces
opnsense_interfaces_lagg_flapping_totalCounterdeviceCumulative count of LAGG active-port membership change (flap) events since the last stat reset. Only emitted when the box reports a lagg statistics block for this device.--exporter.disable-interfaces
opnsense_interfaces_lagg_port_activeGaugedevice, memberWhether this LAGG member port is currently active/selected to carry traffic (1) or not (0). The device label is the owning lagg interface; member is the physical port.--exporter.disable-interfaces
opnsense_interfaces_lagg_port_collectingGaugedevice, memberLACP collecting state (RX distribution enabled) for this LAGG member port (1=collecting, 0=not). Only emitted for LACP laggs whose laggport reported a state; failover/loadbalance laggs never carry this state and emit no series.--exporter.disable-interfaces
opnsense_interfaces_lagg_port_distributingGaugedevice, memberLACP distributing state (TX distribution enabled) for this LAGG member port (1=distributing, 0=not). Only emitted for LACP laggs whose laggport reported a state; failover/loadbalance laggs never carry this state and emit no series.--exporter.disable-interfaces
opnsense_interfaces_bridge_memberGaugedevice, memberWhether this interface is a member of the given bridge(4) interface. Value is always 1. The device label is the owning bridge interface; member is the attached interface.--exporter.disable-interfaces
opnsense_interfaces_sfp_infoGaugedevice, vendor, part_number, serial_numberSFP/SFP+ transceiver identity for this interface's optical cage. Value is always 1. Emitted for any plugged module, optical or copper. Join on the device label.--exporter.disable-interfaces
opnsense_interfaces_sfp_temperature_celsiusGaugedeviceSFP module temperature in degrees Celsius (Digital Optical Monitoring). Only emitted when the transceiver reports a DOM temperature reading; copper RJ45 SFPs never report DOM and emit no series here.--exporter.disable-interfaces
opnsense_interfaces_sfp_voltage_voltsGaugedeviceSFP module supply voltage in volts (Digital Optical Monitoring). Only emitted when the transceiver reports a DOM voltage reading; copper RJ45 SFPs never report DOM and emit no series here.--exporter.disable-interfaces
opnsense_interfaces_sfp_lane_rx_power_milliwattsGaugedevice, laneSFP per-lane received optical power in milliwatts, linear scale (Digital Optical Monitoring). See also sfp_lane_rx_power_dbm for the logarithmic (dBm) reading of the same measurement. Only emitted for lanes with a DOM RX power reading; copper RJ45 SFPs never report DOM and emit no series here.--exporter.disable-interfaces
opnsense_interfaces_sfp_lane_rx_power_dbmGaugedevice, laneSFP per-lane received optical power in dBm, logarithmic scale (Digital Optical Monitoring). See also sfp_lane_rx_power_milliwatts for the linear (mW) reading of the same measurement. Only emitted for lanes with a DOM RX power reading; copper RJ45 SFPs never report DOM and emit no series here. NOTE (#456): before this release this series erroneously published the mW reading under the _dbm name — values will step-change to the correct (and much smaller-magnitude, often negative) dBm figure.--exporter.disable-interfaces
opnsense_interfaces_sfp_lane_tx_bias_milliampsGaugedevice, laneSFP per-lane laser bias current in milliamps (Digital Optical Monitoring). Only emitted for lanes with a DOM TX bias reading; copper RJ45 SFPs never report DOM and emit no series here.--exporter.disable-interfaces

Kea DHCP

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_kea_dhcp4_leases_totalCounter---Total number of Kea DHCPv4 leases--exporter.disable-kea
opnsense_kea_dhcp4_leases_by_interfaceGaugeinterfaceNumber of Kea DHCPv4 leases per interface--exporter.disable-kea
opnsense_kea_dhcp4_leases_reserved_totalCounter---Total number of reserved (static) Kea DHCPv4 leases--exporter.disable-kea
opnsense_kea_dhcp4_leases_dynamic_totalCounter---Total number of dynamic Kea DHCPv4 leases--exporter.disable-kea
opnsense_kea_dhcp4_leases_by_stateGaugestateNumber of Kea DHCPv4 leases per lease state (active, declined, expired-reclaimed)--exporter.disable-kea
opnsense_kea_dhcp4_lease_infoGaugeaddress, hostname, hwaddr, interface, vendor, valid_lifetime, client_idPer-lease DHCPv4 information (value is expire timestamp). Only emitted when --exporter.enable-kea-details is set.--exporter.disable-kea
opnsense_kea_dhcp4_lease_pool_statsGaugepool_stateKea's own DHCPv4 lease pool accounting (the response's top-level stats object, computed over the FULL lease population), by pool_state (active/inactive/total). Authoritative and never truncated by bootgrid pagination, unlike a row-derived count -- a complement to the other DHCPv4 lease metrics in this collector, not a replacement for any of them.--exporter.disable-kea
opnsense_kea_dhcp6_leases_totalCounter---Total number of Kea DHCPv6 leases--exporter.disable-kea
opnsense_kea_dhcp6_leases_by_interfaceGaugeinterfaceNumber of Kea DHCPv6 leases per interface--exporter.disable-kea
opnsense_kea_dhcp6_leases_reserved_totalCounter---Total number of reserved (static) Kea DHCPv6 leases--exporter.disable-kea
opnsense_kea_dhcp6_leases_dynamic_totalCounter---Total number of dynamic Kea DHCPv6 leases--exporter.disable-kea
opnsense_kea_dhcp6_leases_by_stateGaugestateNumber of Kea DHCPv6 leases per lease state (active, declined, expired-reclaimed)--exporter.disable-kea
opnsense_kea_dhcp6_leases_by_typeGaugetypeNumber of Kea DHCPv6 leases per lease type (IA_NA address lease vs IA_PD prefix delegation)--exporter.disable-kea
opnsense_kea_dhcp6_lease_infoGaugeaddress, hostname, hwaddr, interface, vendor, valid_lifetime, prefix_lenPer-lease DHCPv6 information (value is expire timestamp). Only emitted when --exporter.enable-kea-details is set. prefix_len is the delegated prefix's block size (e.g. 56 for a /56) for an IA_PD (prefix-delegation) lease; for an IA_NA (address) lease it carries Kea's fixed 128 (a single address has no delegated block).--exporter.disable-kea
opnsense_kea_dhcp6_lease_pool_statsGaugepool_stateKea's own DHCPv6 lease pool accounting (the response's top-level stats object, computed over the FULL lease population), by pool_state (active/inactive/total). Authoritative and never truncated by bootgrid pagination, unlike a row-derived count -- a complement to the other DHCPv6 lease metrics in this collector, not a replacement for any of them.--exporter.disable-kea
opnsense_kea_service_runningGauge---Whether the Kea DHCP service is running (1 = running, 0 = stopped/disabled)--exporter.disable-kea
opnsense_kea_dhcp4_pool_sizeGaugesubnet, interfaceNumber of addresses in the configured Kea DHCPv4 pools for this subnet--exporter.disable-kea
opnsense_kea_dhcp6_pool_sizeGaugesubnet, interfaceNumber of addresses in the configured Kea DHCPv6 pools for this subnet--exporter.disable-kea
opnsense_kea_dhcp4_pool_usedGaugesubnetNumber of Kea DHCPv4 leases whose address falls within this subnet's configured pool--exporter.disable-kea
opnsense_kea_dhcp6_pool_usedGaugesubnetNumber of Kea DHCPv6 address (non-PD) leases whose address falls within this subnet's configured pool--exporter.disable-kea
opnsense_kea_dhcp6_pd_pool_sizeGaugesubnet, prefixDelegable-prefix capacity of a configured Kea DHCPv6 prefix-delegation pool (2^(delegated_len-prefix_len))--exporter.disable-kea

Kernel Memory (UMA zones and malloc types)

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_kernel_memory_zone_usedGaugezoneItems currently allocated out of this UMA zone. A UMA zone is a kernel pool of fixed-size objects (firewall states, sockets, mbufs, ...). Multiply by opnsense_kernel_memory_zone_item_size_bytes for the bytes in use. Where the box reported the same zone name more than once (one row per NUMA domain, or one per instance of a kernel module's zone) the rows are summed into a single series.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_freeGaugezoneItems sitting free in this UMA zone's caches, already charged to the kernel and available for immediate reuse. A zone with used climbing and free at zero is about to have to ask the VM system for more memory, which is where allocation failures start.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_limitGaugezoneConfigured hard ceiling on items in this UMA zone. ZERO MEANS NO CEILING IS CONFIGURED, not a ceiling of zero - verified on a live OPNsense 26.1 firewall, where 'pf state keys' reports limit 0 while holding 16275 items. Any saturation ratio must therefore skip zones whose limit is 0, e.g. opnsense_kernel_memory_zone_used / (opnsense_kernel_memory_zone_limit > 0). Where several rows were merged into one series the ceilings are summed, which is the correct aggregate ceiling for that zone across the box.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_item_size_bytesGaugezoneSize in bytes of ONE item in this UMA zone - not the zone's total footprint. It is a near-constant property of the kernel build; it is exported so bytes can be derived from the item counts without hard-coding struct sizes per release.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_requests_totalCounterzoneAllocation requests made against this UMA zone since boot, successful or not. Useful as the denominator for a failure ratio and as the churn rate of the underlying resource.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_failures_totalCounterzoneAllocations this UMA zone could NOT satisfy since boot. A non-zero rate means the kernel asked for an object and was refused, and the caller had to give up: on 'pf states' a connection was not tracked, on 'socket' or 'tcp_inpcb' a connection was refused, on an mbuf zone a packet was dropped. Alert on the rate, not the level - the counter never resets before a reboot. Two zone families are noisy by design and are NOT operator-actionable: the 'N Bucket' zones (UMA's own per-CPU cache bucket allocator, which is expected to fail and fall back - all three live boxes show large non-zero values) and 'vm pgcache'. Six zones overlap the mbuf collector, which reads the separate systemMbuf endpoint: mbuf<->type=mbuf, mbuf_packet<->packet, mbuf_cluster<->cluster, mbuf_jumbo_page<->jumbop, mbuf_jumbo_9k<->jumbo9, mbuf_jumbo_16k<->jumbo16 (the mbuf collector's type=sfbufs has no UMA zone). Those are two independent readings of the same kernel counters taken from two endpoints at two different times; treat them as corroboration and NEVER sum the two families together, or every mbuf failure is counted twice.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_sleeps_totalCounterzoneAllocations from this UMA zone that had to BLOCK waiting for memory since boot. The softer twin of zone_failures_total: the allocation eventually succeeded, so nothing was dropped, but the requesting thread stalled - which on a packet path shows up as latency rather than loss. Sleeps rising is the warning that precedes failures. Same overlap rule as zone_failures_total: do not sum with opnsense_mbuf_sleeps_total.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_xdomain_totalCounterzoneAllocations this UMA zone satisfied from a REMOTE NUMA domain since boot, because the local domain had nothing free. Not an error - the allocation succeeded - but on a NUMA box a rising rate means memory is being touched across the interconnect and is a latency, not a correctness, signal. Flat zero on single-domain hardware.--exporter.disable-kernel-memory
opnsense_kernel_memory_zone_failures_all_totalCounter---Allocation failures summed across EVERY UMA zone the box reports, including zones that have no dedicated panel and zones a future kernel adds. It exists so a failure in a zone nobody thought to look at is still visible on one series; use it as the catch-all alert and the per-zone series to find the culprit. It includes the benign 'N Bucket' and 'vm pgcache' families, which on every live box dominate the absolute value - so this counter is only meaningful as a rate, and a baseline above zero is normal.--exporter.disable-kernel-memory
opnsense_kernel_memory_malloc_in_useGaugetypeLive allocations currently held by this kernel malloc type (vmstat -m's InUse). Malloc types are variable-size kernel allocations, as distinct from the fixed-size UMA zones above; a type whose InUse climbs monotonically over weeks of uptime is the shape of a kernel memory leak.--exporter.disable-kernel-memory
opnsense_kernel_memory_malloc_bytesGaugetypeBytes currently held by this kernel malloc type (vmstat -m's MemUse). Reported in bytes by the API despite the OPNsense UI labelling the total 'k'.--exporter.disable-kernel-memory
opnsense_kernel_memory_malloc_requests_totalCountertypeAllocation requests made against this kernel malloc type since boot. There is no per-type failure counter in this payload - vmstat -m does not report one - so malloc-type exhaustion is inferred from in_use/bytes growth, not observed directly. The failure signal lives on the UMA zones above.--exporter.disable-kernel-memory

LLDP Neighbors

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_lldp_neighborsGaugeinterfaceNumber of LLDP neighbors currently seen on this local interface--exporter.disable-lldpd
opnsense_lldp_neighbor_infoGaugeinterface, chassis_name, port_id, port_descrLLDP neighbor information (value is always 1; use labels). SysDescr and MgmtIP are deliberately excluded from labels (free-text/unbounded)--exporter.disable-lldpd

Local Auth

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_auth_usersGaugedisabledNumber of local users, by disabled state (aggregate count only - no usernames are exposed).--exporter.disable-auth
opnsense_auth_admin_usersGauge---Number of local users with administrator privileges (is_admin computed by OPNsense; aggregate count only).--exporter.disable-auth
opnsense_auth_users_expiredGauge---Number of local users whose account expiry date is in the past (aggregate count only).--exporter.disable-auth
opnsense_auth_users_with_otpGauge---Number of local users with a TOTP seed configured (aggregate count only - the seed itself is never read into exporter memory beyond a transient presence check).--exporter.disable-auth
opnsense_auth_api_keysGauge---Total number of local-user API keys configured (aggregate count only - key material is never decoded).--exporter.disable-auth
opnsense_auth_groupsGauge---Total number of local authentication groups configured.--exporter.disable-auth
opnsense_auth_users_shell_warningGauge---Number of local users OPNsense flags with shell_warning: a NON-administrator account that has been given a real login shell (any shell whose path starts with /). Says nothing about which shell, and never fires for an administrator. Aggregate count only - no usernames are exposed.--exporter.disable-auth
opnsense_auth_oldest_password_age_secondsGauge---Age in seconds of the least recently changed local password, across the accounts that have a recorded change time. NOT emitted at all when no account has one - a 0 would claim every password was just rotated. Read alongside users_password_age_unknown, which counts the accounts this maximum cannot see. Aggregate only - no usernames are exposed.--exporter.disable-auth
opnsense_auth_users_password_age_unknownGauge---Number of local users with no usable password-change time. OPNsense records pwd_changed_at only when a password is actually changed, so this counts accounts whose password predates that bookkeeping - the worst posture on the box, and invisible in oldest_password_age_seconds. Aggregate count only - no usernames are exposed.--exporter.disable-auth

Log-derived Events

Mbuf

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_log_events_firewall_totalCounteraction, interface, rule_id, rule_name, scopeFirewall (filterlog) events derived from received syslog, by action, interface, rule and scope. Counts every line including passes; the raw pass lines may be sampled away (--logs.syslog.sample) while this counter still counts them.--exporter.disable-log-events
opnsense_log_events_haproxy_totalCounterevent, backend, server, state, status_classHAProxy events derived from received syslog, by event, backend, server, state and HTTP status class.--exporter.disable-log-events
opnsense_log_events_sshd_totalCounterresult, method, scopesshd authentication events derived from received syslog, by result, method and source scope.--exporter.disable-log-events
opnsense_log_events_dhcp_totalCounteraction, interface, serverDHCP lease events derived from received syslog, by action, interface and server.--exporter.disable-log-events
opnsense_log_events_audit_totalCounterevent, resultAudit/config events derived from received syslog, by event and result.--exporter.disable-log-events
opnsense_log_events_ids_totalCounterevent_type, action, category, severitySuricata IDS/IPS events derived from received syslog, by event type, action, category and severity. Signature text and SID are never labels.--exporter.disable-log-events
opnsense_log_events_gateway_totalCounterevent, gatewaydpinger gateway alarm transitions derived from received syslog, by closed event and configured gateway monitor name. Address, alarm state, RTT and loss stay on the structured log record and are never labels.--exporter.disable-log-events
opnsense_log_events_radius_totalCounterevent, result, client_scopeFreeRADIUS access decisions derived from received syslog, by event, result and client scope.--exporter.disable-log-events
opnsense_log_events_vpn_totalCounterbackend, event, result, connectionIPsec (charon) and OpenVPN tunnel lifecycle transitions derived from received syslog, by backend, closed event, result and configured connection name. event is one of established, terminated, authentication_failed, liveness_failed or certificate_failed; result is success for the first two and failure for the other three. connection is the name configured on the firewall, resolved from the IPsec connection or OpenVPN instance id, and is EMPTY when the id could not be resolved - never a raw UUID. Usernames, certificate subjects and serials, IKE identities, peer addresses and ports, SPIs and daemon error text are never labels; they stay on the shipped log record. Only the grammar captured on OPNsense 27.1.a_40 (strongSwan 6.0.7, OpenVPN 2.7.5) is counted - any other line still ships as a log record but is not counted as an inferred transition.--exporter.disable-log-events
opnsense_log_events_carp_totalCounterevent, from, to, interface, vhidFreeBSD kernel CARP transitions derived from received syslog, by closed event, previous and current state, OS interface device and VHID. event is one of state_changed, demoted or promoted; from and to are master, backup or init, lowercased. A demotion record leaves from, to, interface and vhid EMPTY - the kernel's demotion adjustment is global to the node and names neither an interface nor a VHID. demoted and promoted are distinguished by the SIGN of the kernel's demotion delta: positive raises the node's demotion total, negative lowers it. The kernel's CAUSE for the transition (initialization complete, master timed out, hardware interface up, pfsync bulk start, pfsync bulk fail, service disruption, ...) is deliberately NOT a label: it is open-ended free text across FreeBSD versions. It ships as carp.reason on the log record, alongside carp.demotion.delta and carp.demotion.total - which are the numbers explaining WHY a node demoted, and which opnsense_carp_demotion (a current-state gauge) does not retain. Read this beside the CARP VIP Status timeline: that shows what state the node is in now, this shows the transitions that got it there. Only the grammar captured on OPNsense 27.1.a_40 (FreeBSD 15, net.inet.carp.log=1) is counted - any other kernel line still ships as a log record but is never counted as an inferred transition.--exporter.disable-log-events
opnsense_log_events_upnp_totalCounterevent, result, protocolminiupnpd UPnP IGD / NAT-PMP / PCP mapping events derived from received syslog, by closed event, result and protocol. event is one of expired (a mapping reached the end of its lease and was torn down - the only event whose result is ok), cleanup_failed (the daemon could not find the pf nat or redirect rule it was deleting), unauthorized (a PCP client asked to remove a mapping it does not own) or lease_file_error. protocol is tcp or udp, and EMPTY on the two cleanup-failure grammars and the lease-file error, which name none. Port numbers, the daemon's opaque addr= token, lease-file paths, mapping descriptions and client identities are deliberately NOT labels - an ephemeral port would mint a series per mapping - and ship as upnp.port.external / upnp.port.internal or inside the log record's body. THERE IS NO ACTIVE-MAPPING GAUGE and none can be derived from this: the plugin's own status page runs pfctl to list mappings rather than exposing them through an API, an event stream cannot see pre-existing mappings or survive a daemon restart, and expired is a decrement with no matching increment. A SUCCESSFUL add or delete is likewise absent - miniupnpd logs those at a verbosity OPNsense's os-upnp plugin does not expose, and no attempt line is treated as proof of success. Only the grammar captured on OPNsense 26.7.1_1 and 27.1.a_40 (miniupnpd 2.3.9_2,1) is counted; any other miniupnpd line still ships as a log record but is never counted.--exporter.disable-log-events
opnsense_log_events_zenarmor_totalCounterfamily, action, category, interface, rcode, severity, status_classZenarmor events received over the Elasticsearch receiver, by family (flow/dns/tls/web/ids/voip), action, category, interface, DNS rcode, alert severity and HTTP status class. Fields that do not apply to a family are empty. Zenarmor ships ~2.5-3.3M records/day, so these counters are the way to ask rate questions without querying the raw log stream - and they outlive Loki's retention. Application name, IPs, ports, hostnames, MACs, JA3, session/community/connection ids, URIs and DNS queries are never labels; they stay as structured metadata on the record.--exporter.disable-log-events
opnsense_log_events_zenarmor_device_infoGaugedevice_name, device_category, interfaceClient devices Zenarmor has attributed traffic to recently, one series per device with a constant value of 1. It exists so a dashboard can ENUMERATE devices: device_name is Loki structured metadata, which label_values() cannot list and which was rejected as a Loki index label in #473 because it is not a closed set (live values include Plex-generated DNS names). Bounded on both axes - at most 512 devices tracked, and a device is retired 24h after it was last seen - so a churning name cannot grow the series set without limit; refusals surface as cardinality_capped_total{family=\"zenarmor_device\"}. Filter the log stream itself on the device_name structured metadata field, not on this metric.--exporter.disable-log-events
opnsense_log_events_netmap_ring_full_events_totalCounterdeviceReports the FreeBSD kernel emitted that a netmap host transmit ring was full on this device - Zenarmor's packet-capture datapath having nowhere to put a packet. ONE PER KERNEL LINE, not one per interval: nm_prlim lets two lines through per second and both are counted, because they are two distinct netmap_transmit() calls that each found the ring full (#610 read the pair as one event double-logged; the capture disagrees - their uptime fields differ by ~70us). THIS COUNTS OCCURRENCES, NOT DROPPED PACKETS, and the difference matters: netmap_transmit() logs through nm_prlim(2, ...), which rate-limits the kernel to 2 lines per second, so this counter FLAT-TOPS at 2/s and under-reports hardest exactly when the condition is worst. Read a rise as 'the ring is filling', never as a packet count, and read a flat 2/s as saturation rather than as a bounded problem. Absence of growth means no report, NOT no drops. There is no true drop counter available for this: the kernel bumps IFCOUNTER_OQDROPS, but the ixl driver overrides that counter in its own if_get_counter, so netmap's increment is invisible on the exact 10G interface that drops - live-corroborated, 14 kernel reports since boot against an API-reported dropped-packets of 0. The ring indices from the log line (hwcur, hwtail, qlen) are NOT labels - they change on every occurrence and would mint a series per line - and ship as netmap.hwcur / netmap.hwtail / netmap.qlen on the log record, where hwcur == hwtail is a completely full ring.--exporter.disable-log-events
opnsense_log_events_arp_address_moves_totalCounterinterfaceTimes the FreeBSD kernel reported an IP address moving from one MAC address to another on this interface - its own duplicate-address, MAC-flap and ARP-spoof detector. Polling the ARP table CANNOT replace this: a flap that resolves inside one poll interval leaves a single MAC in the table, so the scrape sees a perfectly healthy entry and the event is invisible. A steady rate usually means two hosts claiming one address, a gateway failing over, or a VM live-migrating; a burst on a user VLAN is worth investigating. The contested IP and BOTH MAC addresses are deliberately NOT labels - they name whichever hosts are fighting over the address, so the value set is unbounded and identifies individual machines - and ship as arp.address / arp.mac.previous / arp.mac.current on the log record, which is where to look to find out who.--exporter.disable-log-events
opnsense_log_events_dhcp_client_totalCounterinterface, typeDHCP messages this firewall's OWN WAN client sent or received, by interface and message type. This is the CLIENT holding the firewall's uplink address, not the DHCP servers it runs for the LAN - those are opnsense_log_events_dhcp_total, and folding the two together would make a WAN renewal storm indistinguishable from LAN lease churn. type is a closed vocabulary resolved in code: discover, request, ack, nak, offer, decline, release, inform. The signal to watch is the REQUEST rate: a healthy lease renews at T1, so a sustained rise is a retransmit storm and a leading indicator of losing the WAN address hours before the lease actually expires (production ran an 11-12 hour storm at ~100x baseline on 2026-07-30 with no telemetry at all). Any nak deserves attention on its own. interface is EMPTY on a received message that named none - dhclient's 'DHCPACK from ' carries no interface, and it is resolved by correlating the daemon PID with the interface its preceding DHCPREQUEST named; empty means the exporter never saw that line, not that the message had no interface. The DHCP server's address is NOT a label and ships as dhcp_client.server on the record. --exporter.disable-log-events
opnsense_log_events_dhcp_client_script_totalCounterinterface, reasondhclient-script invocations on this firewall's WAN client, by interface and reason. reason is dhclient-script(8)'s OWN closed vocabulary, lowercased (bound, renew, rebind, reboot, expire, fail, timeout, stop, release, preinit, arpcheck, arpsend, medium, nbi); an unrecognised token is never turned into a label. This is the lease STATE MACHINE rather than the wire traffic: renew is the healthy steady state, while expire and fail mean the client has given up on the lease and the uplink address is gone or going. Only renew is observed on the production box - a working WAN never reaches the failure reasons - so the rest are modelled from dhclient-script's documented set, not from capture.--exporter.disable-log-events
opnsense_log_events_dhcp_client_lease_bound_timestamp_secondsGaugeinterfaceUnix time at which this interface's WAN DHCP lease last successfully bound, from dhclient's 'bound to
-- renewal in seconds' line. Set only by an actual bind, so it stands still between renewals by design - time() minus this is how long the current lease has been held. Read it alongside opnsense_log_events_dhcp_client_lease_renewal_timestamp_seconds, which is set from the same line. The leased address itself is deliberately NOT a label (it is this firewall's own public IP and changes on re-bind) and ships as dhcp_client.address on the log record.
--exporter.disable-log-events
opnsense_log_events_dhcp_client_lease_renewal_timestamp_secondsGaugeinterfaceUnix time at which this interface's WAN DHCP lease renewal is next due: the time of the last 'bound to' line plus the renewal interval that line carried. A DEADLINE, not a countdown, on purpose - a countdown gauge has to be recomputed against wall-clock at scrape time and a stale one is indistinguishable from a fresh one, which is the exact failure this metric exists to catch. Alert on it directly: ' - time() < 0' means the renewal deadline passed without dhclient reporting a new bind, which is the WAN losing its address in slow motion. This is a deliberate deviation from #541's proposed dhcp_client_lease_expiry_seconds countdown. Note this is the RENEWAL (T1) deadline dhclient reports, not the absolute lease expiry - dhclient does not log the latter, so it is not invented here. --exporter.disable-log-events
opnsense_log_events_dhcp6c_message_totalCounterinterface, direction, typeDHCPv6 messages this firewall's OWN WAN client (dhcp6c) sent or received, by interface, direction and exchange type. This is the IPv6 twin of opnsense_log_events_dhcp_client_total and is deliberately a separate metric: a v4 and a v6 uplink fail independently, and folding them together would hide a v6-only outage behind a healthy v4 rate. direction is sent or received. type is a closed vocabulary resolved in code - solicit, request, renew, rebind, release, information_request - folded from dhcp6c's two spellings of the same exchange, so a Renew and the REPLY answering it share a type. The signal to watch is the ratio: a healthy client sends a Renew and gets a REPLY back, so sent renew rising while received renew stays flat is the v6 uplink going away, hours before the delegated prefix's valid lifetime expires. interface is EMPTY on a received message - dhcp6c's 'Received REPLY for RENEW' names none and it is resolved by correlating the daemon PID with the interface its preceding 'Sending Renew on ' named; empty means the exporter started mid-lease and never saw that line, not that the message had no interface. Only the grammars captured on the production box plus their siblings read off dhcp6c's own format strings are counted; any other dhcp6c line still ships as a log record but is never counted. --exporter.disable-log-events
opnsense_log_events_dhcp6c_event_totalCounterinterface, event, reasondhcp6c prefix-delegation, address-configuration and script events, by interface, closed event and script reason. event is one of prefix_created / prefix_updated (dhcp6c (re)established the delegated prefix), address_added / address_removed (an address derived from that prefix was configured on a downstream interface), or the four script_* events OPNsense's dhcp6c_script.sh logs: script_executing (the reason handler ran), script_connected (the client attached to a server on a REQUEST or SOLICIT), script_prefix_updated (the new prefix was pushed into the interface configuration) and script_ignored (a REASON the script does not handle). reason is dhcp6c_script's OWN closed REASON set lowercased onto the same vocabulary as dhcp6c_message_total's type, plus exit; it is EMPTY on the prefix and address events, which name none, and on script_ignored, whose REASON is by definition a token nobody has closed. INTERFACE MEANS DIFFERENT THINGS PER EVENT and the event label says which: the WAN dhcp6c is renewing on for the prefix and script events, and the DOWNSTREAM device the delegated prefix was applied to (ixl0, ixl0_vlan100) for the address ones - which is the honest reading of each line, since an address really is configured on the LAN device. The configured address and the delegated prefix are NOT labels and ship as dhcp6c.address and dhcp6c.prefix on the log record.--exporter.disable-log-events
opnsense_log_events_dhcp6c_prefix_updated_timestamp_secondsGaugeinterface, prefix_lengthUnix time at which this interface's delegated IPv6 prefix was last created or refreshed, from dhcp6c's 'createupdate a prefix / pltime=, vltime=' line. Set only by an actual delegation update, so it stands still between renewals by design - time() minus this is how long the current delegation has been held. Read it alongside the two expiry gauges set from the same line. THE PREFIX ITSELF IS DELIBERATELY NOT A LABEL even though it is this firewall's own: it changes on re-delegation, which is exactly the event these gauges watch for, so it would churn the series set during the incident. It ships as dhcp6c.prefix on the log record. prefix_length IS a label - it is the delegation SIZE, stable across a re-delegation, and the only thing distinguishing a second differently-sized delegation on the same WAN; two delegations of the SAME size on one interface collapse onto one series, last write wins.
opnsense_log_events_dhcp6c_prefix_preferred_expiry_timestamp_secondsGaugeinterface, prefix_lengthUnix time at which this interface's delegated IPv6 prefix stops being PREFERRED: the time of the last prefix line plus the pltime it carried. A DEADLINE, not a countdown, on purpose - a countdown gauge has to be recomputed against wall-clock at scrape time and a stale one is indistinguishable from a fresh one, which is the exact failure this metric exists to catch. Alert on it directly: ' - time() < 0' means the preferred lifetime lapsed without dhcp6c reporting a refresh, so every downstream address derived from the prefix is now deprecated and new connections will stop choosing it. This is the EARLIER of the two deadlines and the one to page on: an ISP deprecating a prefix ahead of withdrawing it shortens pltime first, which is why the two lifetimes are kept as separate gauges rather than collapsed. --exporter.disable-log-events
opnsense_log_events_dhcp6c_prefix_valid_expiry_timestamp_secondsGaugeinterface, prefix_lengthUnix time at which this interface's delegated IPv6 prefix stops being VALID: the time of the last prefix line plus the vltime it carried. Same deadline-not-countdown shape as the preferred gauge beside it, and the same alert form, ' - time() < 0'. This is the HARD one: when it passes, every address derived from the prefix is removed from every downstream interface and IPv6 is gone across the whole network at once - which is the failure mode with no IPv4 equivalent, because losing a v4 WAN lease costs the firewall one address while losing a delegation costs every LAN its addressing. Expect it to sit ahead of the preferred deadline; the two arriving equal is normal on an ISP that sends pltime == vltime. --exporter.disable-log-events
opnsense_log_events_dhcp6c_address_updated_timestamp_secondsGaugeinterfaceUnix time at which this interface's OWN WAN IPv6 address (an IA_NA lease, not a delegated prefix) was last created or refreshed, from dhcp6c's 'createupdate an address pltime=, vltime=' line (#560) - the address counterpart of opnsense_log_events_dhcp6c_prefix_updated_timestamp_seconds, for a WAN that takes its address directly by DHCPv6 rather than only a delegated prefix. Set only by an actual lease update, so it stands still between renewals by design. THE ADDRESS ITSELF IS DELIBERATELY NOT A LABEL even though it is this firewall's own: it changes on re-bind, which is one of the conditions this gauge watches for. It ships as dhcp6c.address on the log record. There is no prefix_length dimension here - a single address has none.
opnsense_log_events_dhcp6c_address_preferred_expiry_timestamp_secondsGaugeinterfaceUnix time at which this interface's OWN WAN IPv6 address stops being PREFERRED: the time of the last address-lease line plus the pltime it carried. A DEADLINE, not a countdown, same reasoning as the prefix gauge beside it - a countdown recomputed at scrape time is indistinguishable from a stale one. Alert on it directly: ' - time() < 0'. --exporter.disable-log-events
opnsense_log_events_dhcp6c_address_valid_expiry_timestamp_secondsGaugeinterfaceUnix time at which this interface's OWN WAN IPv6 address stops being VALID: the time of the last address-lease line plus the vltime it carried. Same deadline-not-countdown shape as the preferred gauge beside it. When it passes without a refresh, dhcp6c has lost the WAN address itself. THIS SERIES CAN DISAPPEAR ON PURPOSE: unlike every other gauge in this family, an explicit 'remove an address ' line (no lifetime, no interface) clears this interface's row entirely rather than leaving a frozen deadline in place - a frozen gauge would read as a healthy lease that simply stopped renewing. --exporter.disable-log-events
opnsense_log_events_dhcp6_alloc_fail_totalCounterreasonDHCPv6 lease allocations this firewall's kea-dhcp6 SERVER refused, by closed reason - a LAN client that asked for an IPv6 address or prefix and did not get one. reason is no_pools (not one configured pool was usable for that client, normally a client-class or configuration problem) or exhausted (pools were tried and every candidate was already taken). THIS IS EXACTLY ONE INCREMENT PER FAILED ALLOCATION, and that is the whole design: Kea emits a BURST of up to three ALLOC_ENGINE_V6_ALLOC_FAIL_* lines for one failure, all sharing one transaction id, so counting every line would report three failures for one. Only the CAUSE line is counted - alloc_engine.cc guarantees exactly one of ALLOC_ENGINE_V6_ALLOC_FAIL_NO_POOLS and the bare ALLOC_ENGINE_V6_ALLOC_FAIL fires per failure - while the scope line (SUBNET or SHARED_NETWORK) and the optional CLASSES line ship as log records and count nothing. The client's DUID, the transaction id and the subnet are NOT labels: a DUID is unbounded and identifies a client, a tid is unique per exchange and would mint a series per failure, and the subnet is an IPv6 prefix. All three ship as dhcp.duid / dhcp.tid / dhcp.alloc_fail_subnet on the log record, which is where to look to find out WHICH client. Distinct from opnsense_log_events_dhcp6c_* above, which is this firewall's own WAN client rather than the server it runs for the LAN.--exporter.disable-log-events
opnsense_log_events_cardinality_capped_totalCounterfamilyLog events counted into a family's overflow total instead of their own series, because the label tuple was new and the family already held --logs.max-metric-keys distinct tuples. Both receivers are push-based (and syslog over UDP has a spoofable source), so tuple values are sender-controlled and the budget is what stops one sender growing metric state for the life of the process. Nothing is lost: this plus the family's own series is the true event count. Non-zero and rising means the family is saturated and new tuples are no longer individually visible - raise the budget or find what is minting them.--exporter.disable-log-events
opnsense_log_events_cardinality_keysGaugefamilyDistinct label tuples currently tracked for each log_events family. Compare against --logs.max-metric-keys to see saturation coming before opnsense_log_events_cardinality_capped_total starts rising. Tuples are never evicted, so this only grows within a process lifetime.--exporter.disable-log-events
opnsense_log_events_observation_dropped_totalCounterreasonDerived log-metric observations refused by the non-blocking receiver handoff. A refused syslog observation retains its raw record so sampling cannot discard an uncounted event.--exporter.disable-log-events
Metric NameTypeLabelsDescriptionDisable Flag
opnsense_mbuf_currentGauge---Current number of mbufs in use--exporter.disable-mbuf
opnsense_mbuf_cacheGauge---Number of mbufs in cache--exporter.disable-mbuf
opnsense_mbuf_totalGauge---Total number of mbufs available--exporter.disable-mbuf
opnsense_mbuf_maxGauge---Maximum number of mbufs (the mbuf pool's ceiling, mirroring cluster_max). May read 0 on OPNsense >=26.1.11, where upstream removed this key -- that means no ceiling was reported, not a ceiling of zero, so guard any current/max ratio against a zero denominator (#543).--exporter.disable-mbuf
opnsense_mbuf_cluster_currentGauge---Current number of mbuf clusters in use--exporter.disable-mbuf
opnsense_mbuf_cluster_cacheGauge---Number of mbuf clusters in cache--exporter.disable-mbuf
opnsense_mbuf_cluster_totalGauge---Total number of mbuf clusters available--exporter.disable-mbuf
opnsense_mbuf_cluster_maxGauge---Maximum number of mbuf clusters--exporter.disable-mbuf
opnsense_mbuf_failures_totalCountertypeTotal number of mbuf allocation failures by type--exporter.disable-mbuf
opnsense_mbuf_sleeps_totalCountertypeTotal number of mbuf allocation sleeps by type--exporter.disable-mbuf
opnsense_mbuf_bytes_in_useGauge---Number of bytes of memory currently in use by mbufs--exporter.disable-mbuf
opnsense_mbuf_bytes_totalGauge---Total number of bytes of memory available for mbufs--exporter.disable-mbuf
opnsense_mbuf_bytes_in_cacheGauge---Number of bytes of memory sitting in the mbuf allocator's cache -- already charged to the mbuf/cluster/jumbo pools but not currently in use, so available for immediate reuse without a new system allocation. Complements bytes_in_use (currently used) and bytes_total (the ceiling both draw from) (#579).--exporter.disable-mbuf
opnsense_mbuf_pool_currentGaugepoolCurrent number of items in use in this secondary mbuf pool: jumbo9 (9k jumbo clusters), jumbo16 (16k jumbo clusters), or packet (the secondary zone that pre-combines an mbuf+cluster for m_getcl()). These pools are only reported on OPNsense releases whose underlying FreeBSD netstat -m emits them (26.1+); a pool missing from a scrape means the box's release predates it, not that the pool is empty -- distinguish via absence, never read a missing pool as zero (#579).--exporter.disable-mbuf
opnsense_mbuf_pool_cacheGaugepoolNumber of items sitting free in this secondary mbuf pool's cache, ready for immediate reuse without a new system allocation. For pool=\"packet\" this resolves netstat's packet-free field -- its own human-readable text labels the line \"(current/cache)\", the same current/cache shape as the jumbo9/jumbo16 pools, just a differently-spelled JSON key (#579).--exporter.disable-mbuf
opnsense_mbuf_pool_totalGaugepoolTotal number of items ever allocated to this secondary mbuf pool. NEVER reported for pool=\"packet\": that zone borrows memory from the mbuf and cluster zones rather than owning its own allocation, so upstream's netstat -m has no packet-total key and no series is emitted for it (#579).--exporter.disable-mbuf
opnsense_mbuf_pool_maxGaugepoolConfigured ceiling on this secondary mbuf pool. NEVER reported for pool=\"packet\", for the same reason as pool_total. jumbo16's ceiling is read from upstream's jumbo16-limit key and normalised onto this same metric even though jumbo9's equivalent key is spelled jumbo9-max -- verified against FreeBSD's usr.bin/netstat/mbuf.c: both fields come from an otherwise-identical xo_emit format string whose human-readable label reads \"(current/cache/total/max)\" in both cases, so the two JSON keys are one quantity under an inconsistent upstream name, not two different things (#579).--exporter.disable-mbuf
opnsense_mbuf_sendfile_syscalls_totalCounter---Total number of sendfile syscalls--exporter.disable-mbuf
opnsense_mbuf_sendfile_io_totalCounter---Total number of sendfile I/O operations--exporter.disable-mbuf
opnsense_mbuf_sendfile_pages_sent_totalCounter---Total number of pages sent via sendfile--exporter.disable-mbuf

Monit

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_monit_service_runningGauge---Whether the Monit service is running (1 = running, 0 = stopped/disabled)--exporter.disable-monit
opnsense_monit_status_okGauge---Whether the monit httpd was reachable and returned a valid status (1 = ok, 0 = failed/unreachable)--exporter.disable-monit
opnsense_monit_checks_totalGauge---Total number of service checks configured in monit--exporter.disable-monit
opnsense_monit_check_statusGaugename, typeWhether a monit check reports no errors (1 = status field is 0, 0 = error)--exporter.disable-monit
opnsense_monit_check_monitoredGaugename, typeWhether a monit check is actively monitored (1 = monitored, 0 = not monitored)--exporter.disable-monit
opnsense_monit_filesystem_usage_percentGaugename, typeFilesystem block (space) usage percent for a monit filesystem check--exporter.disable-monit
opnsense_monit_filesystem_inode_usage_percentGaugename, typeFilesystem inode usage percent for a monit filesystem check--exporter.disable-monit
opnsense_monit_process_cpu_percentGaugename, typeCPU usage percent for a monit process check (absent until monit's second poll cycle computes a rate)--exporter.disable-monit
opnsense_monit_process_memory_bytesGaugename, typeResident memory usage in bytes for a monit process check--exporter.disable-monit
opnsense_monit_process_uptime_secondsGaugename, typeUptime in seconds for a monit process check--exporter.disable-monit
opnsense_monit_process_threadsGaugename, typeNumber of threads for a monit process check--exporter.disable-monit
opnsense_monit_host_icmp_response_secondsGaugename, typeICMP ping response time in seconds for a monit host check--exporter.disable-monit
opnsense_monit_port_response_secondsGaugename, type, port, protocolPort connection response time in seconds for a monit host check's port test--exporter.disable-monit
opnsense_monit_system_loadGaugename, type, periodSystem load average for a monit system check--exporter.disable-monit
opnsense_monit_system_memory_percentGaugename, typeSystem memory usage percent for a monit system check--exporter.disable-monit
opnsense_monit_system_swap_percentGaugename, typeSystem swap usage percent for a monit system check--exporter.disable-monit
opnsense_monit_system_cpu_percentGaugename, type, modeSystem CPU usage percent by mode for a monit system check--exporter.disable-monit

NDP

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_ndp_entries_totalGauge---Total number of NDP table entries (low-cardinality aggregate, always emitted)--exporter.disable-ndp
opnsense_ndp_entriesGaugeip, mac, interface_description, type, manufacturer, deviceOne series per NDP neighbour entry (value is always 1). Only emitted when --exporter.enable-ndp-details is set (high, churning cardinality from IPv6 privacy addresses). manufacturer is the OUI lookup for the MAC and is the label that identifies a device. device is the raw kernel device and interface_description the assigned name; they diverge on VLAN children and bridges, and only device joins against the interfaces metrics. type reads empty on OPNsense 26.1, which sends no type key on this endpoint.--exporter.disable-ndp

NTP

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_ntp_peer_infoGaugeserver, refid, type, statusNTP peer information (value is always 1)--exporter.disable-ntp
opnsense_ntp_peer_stratumGaugeserverStratum level of the NTP peer--exporter.disable-ntp
opnsense_ntp_peer_when_secondsGaugeserverSeconds since last response from the NTP peer--exporter.disable-ntp
opnsense_ntp_peer_poll_secondsGaugeserverPoll interval in seconds for the NTP peer--exporter.disable-ntp
opnsense_ntp_peer_reachGaugeserverReachability register of the NTP peer (octal decoded to decimal)--exporter.disable-ntp
opnsense_ntp_peer_delay_millisecondsGaugeserverRound-trip delay to the NTP peer in milliseconds--exporter.disable-ntp
opnsense_ntp_peer_offset_millisecondsGaugeserverClock offset relative to the NTP peer in milliseconds--exporter.disable-ntp
opnsense_ntp_peer_jitter_millisecondsGaugeserverDispersion jitter of the NTP peer in milliseconds--exporter.disable-ntp
opnsense_ntp_peers_totalGauge---Total number of NTP peers--exporter.disable-ntp
opnsense_ntp_gps_okGauge---Whether the last NMEA sentence from a GPS refclock reported a valid fix (1 = ok, 0 = no fix). EXPERIMENTAL: derived from OPNsense source, not validated against real GPS hardware (#224). Absent entirely when no GPS refclock is attached/reporting.--exporter.disable-ntp
opnsense_ntp_gps_satellitesGauge---Number of satellites used in the last GPS fix ($GPGGA sentences only; absent for sentences that don't carry a satellite count). EXPERIMENTAL: derived from OPNsense source, not validated against real GPS hardware (#224).--exporter.disable-ntp

NUT UPS

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_nut_service_runningGauge---Whether the NUT service is running (1 = running, 0 = stopped/disabled)--exporter.disable-nut
opnsense_nut_ups_infoGaugemodel, statusUPS device information (always 1)--exporter.disable-nut
opnsense_nut_battery_charge_percentGauge---Battery charge percentage (battery.charge)--exporter.disable-nut
opnsense_nut_battery_runtime_secondsGauge---Estimated battery runtime in seconds (battery.runtime)--exporter.disable-nut
opnsense_nut_battery_voltage_voltsGauge---Battery voltage in volts (battery.voltage)--exporter.disable-nut
opnsense_nut_input_voltage_voltsGauge---Input (mains) voltage in volts (input.voltage)--exporter.disable-nut
opnsense_nut_output_voltage_voltsGauge---Output voltage in volts (output.voltage)--exporter.disable-nut
opnsense_nut_load_percentGauge---UPS load percentage (ups.load)--exporter.disable-nut
opnsense_nut_temperature_celsiusGauge---UPS temperature in degrees Celsius (ups.temperature)--exporter.disable-nut
opnsense_nut_input_frequency_hertzGauge---Input frequency in Hertz (input.frequency)--exporter.disable-nut
opnsense_nut_status_onlineGauge---1 when UPS is online (OL flag in ups.status)--exporter.disable-nut
opnsense_nut_status_on_batteryGauge---1 when UPS is running on battery (OB flag in ups.status)--exporter.disable-nut
opnsense_nut_status_low_batteryGauge---1 when battery is low (LB flag in ups.status)--exporter.disable-nut
opnsense_nut_status_chargingGauge---1 when battery is charging (CHRG flag in ups.status)--exporter.disable-nut
opnsense_nut_status_replace_batteryGauge---1 when battery replacement is recommended (RB flag in ups.status)--exporter.disable-nut

NetBird

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_netbird_management_connectedGauge---Whether this node's netbird daemon has an active connection to the management server (1 = connected, 0 = not)--exporter.disable-netbird
opnsense_netbird_signal_connectedGauge---Whether this node's netbird daemon has an active connection to the signal server (1 = connected, 0 = not)--exporter.disable-netbird
opnsense_netbird_relays_totalGauge---Number of relay servers known to this node's netbird daemon--exporter.disable-netbird
opnsense_netbird_relays_availableGauge---Number of relay servers currently reachable from this node's netbird daemon--exporter.disable-netbird
opnsense_netbird_peers_totalGauge---Number of netbird network peers known to this node--exporter.disable-netbird
opnsense_netbird_peers_connectedGauge---Number of netbird network peers this node currently has an active WireGuard connection to--exporter.disable-netbird
opnsense_netbird_service_runningGauge---Whether the netbird plugin service is running (1 = running, 0 = stopped/disabled)--exporter.disable-netbird
opnsense_netbird_infoGaugecli_version, daemon_versionNetBird node version information (value is always 1; see labels). Only emitted when the daemon reports version data (absent while the daemon itself is down)--exporter.disable-netbird
opnsense_netbird_daemon_stateGaugestateCurrent state of this node's netbird daemon (#455; always 1; exactly one series per scrape). state is drawn from netbird's closed DaemonStatus vocabulary - Idle, Connecting, Connected, NeedsLogin, LoginFailed, SessionExpired - and anything else, including a future upstream state, collapses to unknown. Idle is a normal lazy-connection state, NOT a fault; NeedsLogin/SessionExpired mean the peer needs operator re-authentication. Only emitted when the daemon reported a daemonStatus (absent while the daemon itself is down).--exporter.disable-netbird
opnsense_netbird_peer_connectedGaugefqdnWhether this node currently has an active WireGuard connection to the peer (1 = connected, 0 = not). Only emitted when --exporter.enable-netbird-details is set.--exporter.disable-netbird
opnsense_netbird_peer_directGaugefqdnWhether the connection to the peer uses a direct P2P path (1 = direct, 0 = relayed). Only emitted for peers with an active connection and when --exporter.enable-netbird-details is set.--exporter.disable-netbird
opnsense_netbird_peer_received_bytes_totalCounterfqdnBytes received from this peer since the netbird daemon started. Only emitted when --exporter.enable-netbird-details is set.--exporter.disable-netbird
opnsense_netbird_peer_transmitted_bytes_totalCounterfqdnBytes transmitted to this peer since the netbird daemon started. Only emitted when --exporter.enable-netbird-details is set.--exporter.disable-netbird
opnsense_netbird_peer_last_handshake_timestamp_secondsGaugefqdnUnix timestamp of the last WireGuard handshake with this peer. Only emitted for peers with an active connection and a recorded handshake, and when --exporter.enable-netbird-details is set.--exporter.disable-netbird
opnsense_netbird_peer_latency_secondsGaugefqdnMeasured round-trip latency to this peer. Only emitted for peers with an active connection and a recorded latency, and when --exporter.enable-netbird-details is set.--exporter.disable-netbird

NetFlow

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_netflow_enabledGauge---Whether netflow capture is enabled (1 = enabled, 0 = disabled)--exporter.enable-netflow
opnsense_netflow_local_collection_enabledGauge---Whether local netflow collection is enabled (1 = enabled, 0 = disabled)--exporter.enable-netflow
opnsense_netflow_activeGauge---Whether the netflow service is active (1 = active, 0 = inactive)--exporter.enable-netflow
opnsense_netflow_collectors_countGauge---Number of active netflow collectors--exporter.enable-netflow
opnsense_netflow_cache_packets_totalCounterinterfaceTotal packets observed in netflow cache by interface--exporter.enable-netflow
opnsense_netflow_cache_source_ip_addressesGaugeinterfaceNumber of unique source IP addresses in netflow cache by interface--exporter.enable-netflow
opnsense_netflow_cache_destination_ip_addressesGaugeinterfaceNumber of unique destination IP addresses in netflow cache by interface--exporter.enable-netflow
opnsense_netflow_capture_expectedGaugeinterfaceWhether the box is configured to capture NetFlow on this interface (1 = selected, 0 = listed in the config but not selected). This is the half no counter can supply: an interface producing nothing is indistinguishable from an idle one unless you know it was supposed to be producing something. The set is closed - it comes from the firewall's own netflow config - so cardinality is bounded by the number of interfaces. An interface with records but capture_expected=0 is NOT a fault: ng_netflow names the far side of a flow from a FIB lookup, so an interface can be labelled without being captured on.--exporter.enable-netflow
opnsense_netflow_capture_last_record_secondsGaugeinterfaceSeconds since this exporter last received a NetFlow record attributed to this interface. A RAW AGE, not a verdict: a guest VLAN can be legitimately silent for hours, so the threshold is the operator's to choose - compare against capture_active_timeout_seconds. Interfaces that have produced NO record since the exporter started are ABSENT here rather than reported as a large age, because every interface is silent at startup and a number there would make a fresh boot look like a box-wide outage; \"never seen\" and \"seen, then stopped\" are different states. IMPORTANT: a fresh age proves the interface is being NAMED, not that its own capture hook is alive - ng_netflow fills one side of each flow from a FIB lookup, so records captured elsewhere name it too. For per-hook liveness use opnsense_netflow_cache_packets_total, which is the box's own per-node view.--exporter.enable-netflow
opnsense_netflow_capture_active_timeout_secondsGauge---The box's configured ACTIVE flow timeout: how long a long-running flow sits in the cache before ng_netflow exports it anyway. Exported so a \"this interface has gone quiet\" threshold is derived from what the firewall actually applies rather than guessed - an interface cannot be judged silent until well past this.--exporter.enable-netflow
opnsense_netflow_capture_inactive_timeout_secondsGauge---The box's configured INACTIVE flow timeout: how long an idle flow waits before export. The floor on how quickly any interface can be observed to have stopped.--exporter.enable-netflow

Network Diagnostics

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_network_diag_netisr_dispatched_totalCounterprotocolTotal number of netisr dispatches by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_hybrid_dispatched_totalCounterprotocolTotal number of netisr hybrid dispatches by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queued_totalCounterprotocolTotal number of netisr packets queued by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_handled_totalCounterprotocolTotal number of netisr packets handled by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queue_drops_totalCounterprotocolTotal number of netisr queue drops by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queue_lengthGaugeprotocolCurrent maximum netisr queue length across workstreams by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queue_watermarkGaugeprotocolHigh watermark of netisr queue length across workstreams by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queue_limitGaugeprotocolConfigured netisr queue limit by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_protocol_infoGaugeprotocol, protocol_id, policy, policy_type, flagsStatic netisr protocol configuration (value is always 1). Join on protocol to interpret the other netisr series. policy_type is the one that matters: cpu and flow protocols are meant to spread across every netisr workstream, whereas source protocols are single-lane BY DESIGN and will always show one active workstream — alerting on workstream imbalance without excluding them fires falsely on igmp/rtsock/arp on every box. policy is the dispatch mode (direct/hybrid/deferred/default) and flags the raw netisr flag string.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_active_workstreamsGaugeprotocolNumber of netisr workstreams that have carried any traffic for this protocol (handled, queued, dispatched or hybrid-dispatched greater than zero). Compare against the CPU count: a cpu-policy protocol showing 4 on a 12-core box means eight cores do no work for it at all, which is a net.isr.maxthreads/bindthreads or NIC RSS problem, not a queue-size problem. A value of 1 is normal and expected for source-policy protocols.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_workstreams_at_limitGaugeprotocolNumber of netisr workstreams whose high watermark has reached this protocol's configured queue limit. Non-zero means at least one lane has actually run out of queue at some point since boot. Because the watermark is a since-boot high water mark and never decays, this stays non-zero after the condition clears — it records that it happened, not that it is happening. Always 0 when the queue limit is 0 (unset).--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_queue_imbalance_ratioGaugeprotocolMaximum netisr queue watermark divided by the mean watermark, computed over ACTIVE workstreams only. 1.0 is a perfectly even spread across the lanes that are working; higher means one lane absorbs disproportionately more depth. Idle workstreams are excluded on purpose — including them would dilute the mean and make an affinity problem look milder the more CPUs the box has. Emitted as 0 when the ratio is undefined: fewer than two active workstreams, or every active watermark still zero. Read alongside netisr_active_workstreams, which is what tells you whether a 0 means 'even' or 'single-lane'.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_drop_concentration_ratioGaugeprotocolLargest per-workstream netisr queue-drop count divided by the total across all workstreams. 1.0 means every drop landed on a single lane, which points at CPU affinity — raising net.isr.maxqlen would mask the symptom rather than fix it. A value near 1/N means the drops are spread and the protocol is genuinely over its queue limit. Emitted as 0 when nothing has dropped, so gate any alert on the drop counter itself, not on this ratio alone.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_dispatched_totalCounterprotocol, cpu, workstreamPackets dispatched directly on this netisr workstream, without being queued. Per-workstream breakdown of netisr_dispatched_total.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_hybrid_dispatched_totalCounterprotocol, cpu, workstreamPackets hybrid-dispatched on this netisr workstream (handled inline because the queue was empty). Per-workstream breakdown of netisr_hybrid_dispatched_total.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_queued_totalCounterprotocol, cpu, workstreamPackets enqueued onto this netisr workstream for deferred processing. Per-workstream breakdown of netisr_queued_total.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_handled_totalCounterprotocol, cpu, workstreamPackets processed by this netisr workstream. Per-workstream breakdown of netisr_handled_total. A row is emitted for EVERY workstream the box reports, including ones that are entirely idle — a run of zero-valued CPUs is the finding, not missing data, and suppressing those rows would hide exactly the affinity problem this breakdown exists to show. Labelled with both workstream (the netisr stream index) and cpu (the core it is bound to); they coincide on a default configuration but are not the same thing when net.isr.bindthreads is off.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_queue_drops_totalCounterprotocol, cpu, workstreamPackets dropped because this netisr workstream's queue was full. Per-workstream breakdown of netisr_queue_drops_total — this is the series that shows whether drops are concentrated on one core or spread across all of them.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_queue_lengthGaugeprotocol, cpu, workstreamInstantaneous depth of this netisr workstream's queue at scrape time. Almost always 0 on a healthy box and a poor sampling of a bursty signal — use netisr_cpu_queue_watermark for whether the queue has ever filled.--exporter.enable-network-diagnostics
opnsense_network_diag_netisr_cpu_queue_watermarkGaugeprotocol, cpu, workstreamHighest queue depth this netisr workstream has ever reached. A since-boot high water mark: it never decays, so it records that the queue filled, not that it is full now. Equal to the protocol's queue limit means this lane has hit its ceiling and dropped.--exporter.enable-network-diagnostics
opnsense_network_diag_sockets_activeGaugetypeNumber of active sockets by type--exporter.enable-network-diagnostics
opnsense_network_diag_sockets_unix_totalGauge---Total number of active Unix domain sockets--exporter.enable-network-diagnostics
opnsense_network_diag_routes_totalGaugeprotoNumber of routing table entries by protocol--exporter.enable-network-diagnostics
opnsense_network_diag_interface_routesGaugeproto, device, interfaceNumber of routing table entries whose egress interface is this one, by address family. Per-interface breakdown of routes_total, which remains the sum. device is the raw kernel device and interface the assigned description; they diverge on VLAN children, PPPoE links and unassigned devices, and only device joins against the interfaces metrics. A count collapsing to near zero on a WAN device is a link that has dropped its learned routes.--exporter.enable-network-diagnostics
opnsense_network_diag_routes_by_flagsGaugeproto, flagsNumber of routing table entries carrying this exact netstat flag string, by address family. The flag vocabulary is closed and short, so this stays bounded regardless of table size. The letters are netstat's: U up, G gatewayed, H host route, S static, B blackhole (traffic silently discarded), 1 protocol-specific. B is the one worth watching — blackhole routes are usually deliberate but a growing count is traffic being dropped with no other signal. The flags are matched as a whole string, not per letter, so use a regex matcher to select a single flag.--exporter.enable-network-diagnostics
opnsense_network_diag_default_route_presentGaugeprotoWhether a default route exists for this address family (1) or not (0). Losing the default route is a total-outage condition for that family with no other signal in this exporter, so the series is emitted as 0 rather than disappearing — alert on == 0 directly, no absent() rule needed. Emitted for both ipv4 and ipv6 on every scrape; a box with no IPv6 upstream legitimately and permanently reads 0 for ipv6, so scope any alert to the families the box is expected to route.--exporter.enable-network-diagnostics
opnsense_network_diag_default_route_infoGaugeproto, device, interface, gatewayIdentity of each installed default route (value is always 1): which interface it leaves by and which gateway it points at. There is normally exactly one series per address family; two or more means multiple default routes are installed. The gateway IS carried as a label here — unlike every other route, of which there may be hundreds and whose destinations churn — because a change of default gateway is itself the event an operator wants to see. No series is emitted for a family with no default route; read default_route_present for that.--exporter.enable-network-diagnostics
opnsense_network_diag_pfsync_nodes_totalGauge---Total number of pfsync cluster nodes--exporter.enable-network-diagnostics
opnsense_network_diag_pfsync_node_infoGaugecreatorid, is_localPFSync node information (value is always 1)--exporter.enable-network-diagnostics

Nginx

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_nginx_service_runningGauge---Whether the nginx service is running (1 = running, 0 = stopped/disabled)--exporter.disable-nginx
opnsense_nginx_connections_activeGauge---Current active connections--exporter.disable-nginx
opnsense_nginx_connections_readingGauge---Current connections reading request headers--exporter.disable-nginx
opnsense_nginx_connections_writingGauge---Current connections writing responses--exporter.disable-nginx
opnsense_nginx_connections_waitingGauge---Current idle (keep-alive) connections--exporter.disable-nginx
opnsense_nginx_connections_accepted_totalCounter---Cumulative accepted connections--exporter.disable-nginx
opnsense_nginx_connections_handled_totalCounter---Cumulative handled connections--exporter.disable-nginx
opnsense_nginx_requests_totalCounter---Cumulative total requests--exporter.disable-nginx
opnsense_nginx_shared_memory_max_bytesGauge---Maximum size of the VTS shared memory zone in bytes--exporter.disable-nginx
opnsense_nginx_shared_memory_used_bytesGauge---Currently used bytes of the VTS shared memory zone--exporter.disable-nginx
opnsense_nginx_shared_memory_used_nodesGauge---Number of nodes in use in the VTS shared memory zone--exporter.disable-nginx
opnsense_nginx_server_zone_requests_totalCounterzoneCumulative requests to this server zone--exporter.disable-nginx
opnsense_nginx_server_zone_bytes_in_totalCounterzoneCumulative bytes received by this server zone--exporter.disable-nginx
opnsense_nginx_server_zone_bytes_out_totalCounterzoneCumulative bytes sent by this server zone--exporter.disable-nginx
opnsense_nginx_server_zone_responses_totalCounterzone, codeCumulative responses by HTTP status code class for this server zone--exporter.disable-nginx
opnsense_nginx_upstream_server_requests_totalCounterupstream, serverCumulative requests forwarded to this upstream server--exporter.disable-nginx
opnsense_nginx_upstream_server_bytes_in_totalCounterupstream, serverCumulative bytes received from this upstream server--exporter.disable-nginx
opnsense_nginx_upstream_server_bytes_out_totalCounterupstream, serverCumulative bytes sent to this upstream server--exporter.disable-nginx
opnsense_nginx_upstream_server_responses_totalCounterupstream, server, codeCumulative responses from this upstream server by HTTP status code class--exporter.disable-nginx
opnsense_nginx_upstream_server_downGaugeupstream, serverWhether this upstream server is marked down (1 = down, 0 = up)--exporter.disable-nginx
opnsense_nginx_upstream_server_response_time_secondsGaugeupstream, serverAverage response time of this upstream server in seconds--exporter.disable-nginx
opnsense_nginx_server_zone_cache_responses_totalCounterzone, cache_statusCumulative responses by cache status for this server zone (only when the vts build reports cache status)--exporter.disable-nginx
opnsense_nginx_server_zone_request_seconds_totalCounterzoneCumulative sum of request processing time in seconds for this server zone--exporter.disable-nginx
opnsense_nginx_server_zone_counter_wraps_totalCounterzoneCumulative count of times one of this server zone's own vts counters (requests/bytes/response-code/request-time) has been detected to wrap past its accumulated value (nginx-module-vts's own overCounts wrap-detection counter). A non-zero value here means a rate() over this zone's other counters has a discontinuity at the wrap point.--exporter.disable-nginx
opnsense_nginx_upstream_server_request_seconds_totalCounterupstream, serverCumulative sum of request processing time in seconds for this upstream server--exporter.disable-nginx
opnsense_nginx_upstream_server_response_seconds_totalCounterupstream, serverCumulative sum of response time in seconds for this upstream server--exporter.disable-nginx
opnsense_nginx_upstream_server_counter_wraps_totalCounterupstream, serverCumulative count of times one of this upstream server's own vts counters has been detected to wrap past its accumulated value (nginx-module-vts's own overCounts wrap-detection counter). See server_zone_counter_wraps_total for the sibling metric on server zones.--exporter.disable-nginx
opnsense_nginx_cache_zone_max_bytesGaugezoneMaximum size of this proxy_cache_path zone in bytes--exporter.disable-nginx
opnsense_nginx_cache_zone_used_bytesGaugezoneCurrently used bytes of this proxy_cache_path zone--exporter.disable-nginx
opnsense_nginx_cache_zone_bytes_in_totalCounterzoneCumulative bytes received for this cache zone--exporter.disable-nginx
opnsense_nginx_cache_zone_bytes_out_totalCounterzoneCumulative bytes sent for this cache zone--exporter.disable-nginx
opnsense_nginx_cache_zone_responses_totalCounterzone, cache_statusCumulative responses by cache status for this cache zone--exporter.disable-nginx
opnsense_nginx_config_load_timestamp_secondsGauge---Unix timestamp of the last nginx config (re)load, as reported by the vts module--exporter.disable-nginx
opnsense_nginx_bansGauge---Current number of active autoblock bans--exporter.disable-nginx
opnsense_nginx_ban_last_timestamp_secondsGauge---Unix timestamp of the most recent autoblock ban--exporter.disable-nginx

OpenVPN

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_openvpn_instancesGaugeuuid, role, description, device_typeOpenVPN instances (1 = enabled, 0 = disabled) by role (server, client)--exporter.disable-openvpn
opnsense_openvpn_instance_max_clientsGaugeuuid, role, description, device_typeConfigured concurrent-client cap for this OpenVPN server instance. Divide opnsense_openvpn_sessions_by_instance by this (joined on description) for a utilization ratio. Only emitted for a server instance with a cap actually configured; an unlimited/uncapped server emits no series here.--exporter.disable-openvpn
opnsense_openvpn_sessionsGaugedescription, real_address, virtual_address, virtual_ipv6_address, usernameOpenVPN session (1 = ok, 0 = not ok). Only emitted when --exporter.enable-openvpn-details is set.--exporter.disable-openvpn
opnsense_openvpn_sessions_totalGauge---Total number of OpenVPN sessions--exporter.disable-openvpn
opnsense_openvpn_sessions_by_instanceGaugedescriptionNumber of OpenVPN sessions per instance--exporter.disable-openvpn
opnsense_openvpn_session_received_bytes_totalCounterdescription, real_address, virtual_address, virtual_ipv6_address, usernameCumulative bytes received for this OpenVPN session (resets on reconnect). Only emitted when --exporter.enable-openvpn-details is set.--exporter.disable-openvpn
opnsense_openvpn_session_transmitted_bytes_totalCounterdescription, real_address, virtual_address, virtual_ipv6_address, usernameCumulative bytes transmitted for this OpenVPN session (resets on reconnect). Only emitted when --exporter.enable-openvpn-details is set.--exporter.disable-openvpn
opnsense_openvpn_session_connected_since_timestamp_secondsGaugedescription, real_address, virtual_address, virtual_ipv6_address, usernameUnix timestamp of when this OpenVPN session connected. Only emitted when --exporter.enable-openvpn-details is set.--exporter.disable-openvpn
opnsense_openvpn_instance_received_bytes_totalCounterdescriptionCumulative bytes received across all active sessions on this OpenVPN instance--exporter.disable-openvpn
opnsense_openvpn_instance_transmitted_bytes_totalCounterdescriptionCumulative bytes transmitted across all active sessions on this OpenVPN instance--exporter.disable-openvpn

PF Statistics

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_pf_stats_state_table_entriesGauge---Current number of entries in the PF state table--exporter.disable-pf-stats
opnsense_pf_stats_state_table_searches_totalCounter---Total number of state table searches--exporter.disable-pf-stats
opnsense_pf_stats_state_table_inserts_totalCounter---Total number of state table inserts--exporter.disable-pf-stats
opnsense_pf_stats_state_table_removals_totalCounter---Total number of state table removals--exporter.disable-pf-stats
opnsense_pf_stats_source_tracking_entriesGauge---Current number of entries in the source tracking table--exporter.disable-pf-stats
opnsense_pf_stats_counter_totalCountercounterTotal count of PF counter by name--exporter.disable-pf-stats
opnsense_pf_stats_limit_counter_totalCountercounterTotal count of PF limit counter by name--exporter.disable-pf-stats
opnsense_pf_stats_memory_limitGaugepoolPF memory pool limit by pool name--exporter.disable-pf-stats
opnsense_pf_stats_timeout_secondsGaugenamePF timeout value in seconds by name--exporter.disable-pf-stats

Protocol Statistics

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_protocol_tcp_connection_count_by_stateGaugestateNumber of TCP connections by state--exporter.disable-protocol
opnsense_protocol_tcp_sent_packets_totalCounter---Number of sent TCP packets--exporter.disable-protocol
opnsense_protocol_tcp_received_packets_totalCounter---Number of received TCP packets--exporter.disable-protocol
opnsense_protocol_arp_sent_requests_totalCounter---Number of sent ARP requests--exporter.disable-protocol
opnsense_protocol_arp_received_requests_totalCounter---Number of received ARP requests--exporter.disable-protocol
opnsense_protocol_icmp_calls_totalCounter---Number of ICMP calls--exporter.disable-protocol
opnsense_protocol_icmp_sent_packets_totalCounter---Number of sent ICMP packets--exporter.disable-protocol
opnsense_protocol_icmp_dropped_by_reason_totalCounterreasonNumber of dropped ICMP packets by reason--exporter.disable-protocol
opnsense_protocol_udp_delivered_packets_totalCounter---Number of delivered UDP packets--exporter.disable-protocol
opnsense_protocol_udp_output_packets_totalCounter---Number of output UDP packets--exporter.disable-protocol
opnsense_protocol_udp_received_datagrams_totalCounter---Number of received UDP datagrams--exporter.disable-protocol
opnsense_protocol_udp_dropped_by_reason_totalCounterreasonNumber of dropped UDP packets by reason--exporter.disable-protocol
opnsense_protocol_carp_received_packets_totalCounteraddress_familyNumber of received CARP packets--exporter.disable-protocol
opnsense_protocol_carp_sent_packets_totalCounteraddress_familyNumber of sent CARP packets--exporter.disable-protocol
opnsense_protocol_carp_dropped_by_reason_totalCounterreasonNumber of dropped CARP packets by reason--exporter.disable-protocol
opnsense_protocol_pfsync_received_packets_totalCounteraddress_familyNumber of received Pfsync packets--exporter.disable-protocol
opnsense_protocol_pfsync_sent_packets_totalCounteraddress_familyNumber of sent Pfsync packets--exporter.disable-protocol
opnsense_protocol_pfsync_dropped_by_reason_totalCounterreasonNumber of dropped Pfsync packets by reason--exporter.disable-protocol
opnsense_protocol_pfsync_send_errors_totalCounter---Number of Pfsync send errors--exporter.disable-protocol
opnsense_protocol_ip_received_packets_totalCounter---Number of received IP packets--exporter.disable-protocol
opnsense_protocol_ip_forwarded_packets_totalCounter---Number of forwarded IP packets--exporter.disable-protocol
opnsense_protocol_ip_sent_packets_totalCounter---Number of sent IP packets--exporter.disable-protocol
opnsense_protocol_ip_dropped_by_reason_totalCounterreasonNumber of dropped IP packets by reason--exporter.disable-protocol
opnsense_protocol_ip_fragments_received_totalCounter---Number of received IP fragments--exporter.disable-protocol
opnsense_protocol_ip_reassembled_packets_totalCounter---Number of reassembled IP packets--exporter.disable-protocol
opnsense_protocol_ip6_received_packets_totalCounter---Number of received IPv6 packets--exporter.disable-protocol
opnsense_protocol_ip6_forwarded_packets_totalCounter---Number of forwarded IPv6 packets--exporter.disable-protocol
opnsense_protocol_ip6_sent_packets_totalCounter---Number of sent IPv6 packets--exporter.disable-protocol
opnsense_protocol_ip6_dropped_by_reason_totalCounterreasonNumber of dropped IPv6 packets by reason--exporter.disable-protocol
opnsense_protocol_ip6_fragments_received_totalCounter---Number of received IPv6 fragments--exporter.disable-protocol
opnsense_protocol_ip6_reassembled_packets_totalCounter---Number of reassembled IPv6 packets--exporter.disable-protocol
opnsense_protocol_icmp6_calls_totalCounter---Number of ICMPv6 calls--exporter.disable-protocol
opnsense_protocol_icmp6_dropped_by_reason_totalCounterreasonNumber of dropped ICMPv6 packets by reason--exporter.disable-protocol
opnsense_protocol_tcp_connection_requests_totalCounter---Number of TCP connection requests--exporter.disable-protocol
opnsense_protocol_tcp_connection_accepts_totalCounter---Number of TCP connection accepts--exporter.disable-protocol
opnsense_protocol_tcp_connections_established_totalCounter---Number of TCP connections established--exporter.disable-protocol
opnsense_protocol_tcp_connections_closed_totalCounter---Number of TCP connections closed--exporter.disable-protocol
opnsense_protocol_tcp_connection_drops_totalCounter---Number of TCP connection drops--exporter.disable-protocol
opnsense_protocol_tcp_connection_drops_by_reason_totalCounterreasonCumulative TCP connections dropped by reason (retransmit_timeout, persist_timeout, finwait2_timeout, keepalive). These are kernel-lifetime counters that reset on reboot, like the other protocol counters. A reason is only emitted when the box reports that wire field; an older box that omits a reason omits its series rather than reporting a fabricated zero (#374).--exporter.disable-protocol
opnsense_protocol_tcp_retransmit_timeouts_totalCounter---Number of TCP retransmit timeouts--exporter.disable-protocol
opnsense_protocol_tcp_keepalive_timeouts_totalCounter---Number of TCP keepalive timeouts--exporter.disable-protocol
opnsense_protocol_tcp_listen_queue_overflows_totalCounter---Number of TCP listen queue overflows--exporter.disable-protocol
opnsense_protocol_tcp_syncache_entries_totalCounter---Number of TCP syncache entries added--exporter.disable-protocol
opnsense_protocol_arp_sent_failures_totalCounter---Number of ARP sent failures--exporter.disable-protocol
opnsense_protocol_arp_sent_replies_totalCounter---Number of ARP sent replies--exporter.disable-protocol
opnsense_protocol_arp_received_replies_totalCounter---Number of ARP received replies--exporter.disable-protocol
opnsense_protocol_arp_received_packets_totalCounter---Number of ARP received packets--exporter.disable-protocol
opnsense_protocol_arp_dropped_no_entry_totalCounter---Number of ARP packets dropped with no entry--exporter.disable-protocol
opnsense_protocol_arp_entries_timeout_totalCounter---Number of ARP entries that timed out--exporter.disable-protocol
opnsense_protocol_tcp_sent_data_bytes_totalCounter---Total bytes of data sent via TCP--exporter.disable-protocol
opnsense_protocol_tcp_retransmitted_packets_totalCounter---Total number of TCP packets retransmitted--exporter.disable-protocol
opnsense_protocol_tcp_retransmitted_bytes_totalCounter---Total bytes retransmitted via TCP--exporter.disable-protocol
opnsense_protocol_tcp_received_in_sequence_bytes_totalCounter---Total bytes received in sequence via TCP--exporter.disable-protocol
opnsense_protocol_tcp_received_duplicate_bytes_totalCounter---Total completely duplicate bytes received via TCP--exporter.disable-protocol
opnsense_protocol_tcp_segments_updated_rtt_totalCounter---Total TCP segments that updated RTT--exporter.disable-protocol
opnsense_protocol_tcp_bad_connection_attempts_totalCounter---Total bad TCP connection attempts--exporter.disable-protocol
opnsense_protocol_tcp_keepalive_probes_totalCounter---Total TCP keepalive probes sent--exporter.disable-protocol
opnsense_protocol_tcp_syncache_dropped_totalCounter---Total TCP syncache entries dropped--exporter.disable-protocol
opnsense_protocol_ip_sent_fragments_totalCounter---Total IP fragments sent--exporter.disable-protocol
opnsense_protocol_arp_dropped_duplicate_address_totalCounter---Total ARP packets dropped due to duplicate address--exporter.disable-protocol
opnsense_protocol_tcp_ecn_packets_totalCounterdirection, markTotal TCP packets carrying an ECN mark, by direction and mark. The sent direction is only emitted on OPNsense 26.1.11+.--exporter.disable-protocol
opnsense_protocol_tcp_ecn_accecn_handshakes_totalCountermarkTotal TCP AccECN (FreeBSD 15) handshake SYNs by mark. Only emitted on OPNsense 26.1.11+.--exporter.disable-protocol
opnsense_protocol_tcp_syncookies_totalCounterresultTotal TCP SYN cookies by result. Only emitted on OPNsense 26.7+.--exporter.disable-protocol
opnsense_protocol_tcp_received_acks_for_data_totalCounterreasonTotal TCP ACKs received for data by reason. Only emitted on OPNsense 26.7+.--exporter.disable-protocol
opnsense_protocol_tcp_sack_recovery_episodes_totalCounter---Total times TCP entered SACK-based loss recovery. Each episode is one burst of loss the peer reported via selective ACK, however many segments it covered — so this counts loss EVENTS while tcp_sack_segment_retransmits_total counts their cost. A rising rate here with a flat retransmit rate means frequent small losses; the reverse means rare but severe ones.--exporter.disable-protocol
opnsense_protocol_tcp_sack_segment_retransmits_totalCounter---Total TCP segments retransmitted during SACK recovery. A subset of tcp_retransmitted_packets_total (which also counts plain RTO-driven retransmission) — the two overlap, so never sum them.--exporter.disable-protocol
opnsense_protocol_tcp_sack_retransmitted_bytes_totalCounter---Total bytes retransmitted during TCP SACK recovery. A subset of tcp_retransmitted_bytes_total; the two overlap, so never sum them. Compare against tcp_sent_data_bytes_total for a SACK-driven retransmission ratio — a sustained rise is a direct TCP-health signal on a box whose job is forwarding packets.--exporter.disable-protocol
opnsense_protocol_tcp_sack_blocks_totalCounterdirectionTotal TCP SACK option blocks, by direction. direction=\"received\" is the peer telling us about holes in the data WE sent (outbound loss); direction=\"sent\" is us telling the peer about holes in the data IT sent (inbound loss). The direction that rises tells you which way the lossy path runs, which no other metric on this box distinguishes.--exporter.disable-protocol
opnsense_protocol_tcp_sack_scoreboard_overflows_totalCounter---Total times the TCP SACK scoreboard overflowed and the kernel stopped tracking individual holes in a connection, falling back to coarser recovery. Should be flat at zero; any sustained rise means loss severe enough that SACK recovery is degrading rather than helping.--exporter.disable-protocol
opnsense_protocol_tcp_sack_lost_retransmissions_totalCounter---Total TCP retransmissions that were themselves lost, detected during SACK recovery. This is the severe case: the path dropped the repair packet too, so recovery stalls until a timeout fires and the connection visibly hangs. No other counter on this box distinguishes a lost repair from ordinary loss. A subset of tcp_retransmitted_packets_total and of tcp_sack_segment_retransmits_total — all three overlap, so never sum them.--exporter.disable-protocol
opnsense_protocol_tcp_sack_tso_chunk_retransmits_totalCounter---Total TCP segments retransmitted as part of a TSO (TCP segmentation offload) chunk during SACK recovery, i.e. repairs the NIC re-segmented rather than the kernel. Low signal on its own; it exists so the sack section is modelled completely and the split between offloaded and kernel-driven repair is visible when tuning TSO. A subset of tcp_sack_segment_retransmits_total — the two overlap, so never sum them.--exporter.disable-protocol
opnsense_protocol_tcp_hostcache_entries_added_totalCounter---Total entries added to the TCP host cache, which remembers per-peer RTT/ssthresh so a new connection to a known peer warm-starts instead of re-probing. The add rate is a proxy for distinct-peer churn, not for connection volume: a repeat peer is a cache hit and adds nothing.--exporter.disable-protocol
opnsense_protocol_tcp_hostcache_buffer_overflows_totalCounter---Total TCP host cache insertions that overflowed a hash bucket and evicted an existing entry. Nonzero means the cache is under churn pressure and the kernel is losing cached path metrics, so affected connections restart from defaults and re-probe the path.--exporter.disable-protocol
opnsense_protocol_tcp_hostcache_hits_totalCountermetricTotal TCP connections that warm-started a path metric from the host cache instead of re-probing it, by which metric was reused: \"rtt\" (round-trip time), \"rttvar\" (RTT variance) and \"ssthresh\" (slow-start threshold). Despite the name these are NOT part of the statistics.tcp.hostcache section — they are top-level counters (connections-hostcache-*) and are the hit side of the cache whose insert and eviction rates tcp_hostcache_entries_added_total and tcp_hostcache_buffer_overflows_total report. A high hit rate is good: it means repeat peers skip re-probing. Hits falling while tcp_hostcache_buffer_overflows_total rises means eviction pressure is destroying the cache's value. The three are counted independently, so they do not sum to a connection count.--exporter.disable-protocol
opnsense_protocol_tcp_timewait_events_totalCountereventTotal TCP TIME_WAIT state events, by kind. \"responds\" = a segment answered from TIME_WAIT (usually a retransmitted FIN, normal); \"recycles\" = a TIME_WAIT entry reused early to make room, which means state pressure from short-lived connection churn; \"resets\" = an RST issued from TIME_WAIT. Only recycles and resets indicate a problem — responds is ordinary close churn.--exporter.disable-protocol
opnsense_protocol_tcp_pmtud_blackhole_events_totalCountereventTotal TCP path-MTU-discovery blackhole detection events, by kind. A PMTUD blackhole is a path that silently drops oversized packets without returning the ICMP \"fragmentation needed\" that would let TCP shrink its MSS — the classic cause of \"some sites load, some hang\", and invisible from outside the box. \"activated\" = the kernel suspected a blackhole and dropped MSS; \"activated_min_mss\" = it fell all the way back to the minimum MSS, so the path is badly broken and throughput will suffer; \"failed\" = shrinking the MSS did not fix it. Any sustained rise warrants investigating MTU on the upstream path.--exporter.disable-protocol
opnsense_protocol_tcp_signature_totalCounterresultTotal TCP-MD5 (RFC 2385) signature outcomes, by result. In practice TCP-MD5 authenticates BGP sessions, so on a box with no MD5-authenticated peer every result is permanently zero and that is correct, not a fault. \"good\" is the healthy denominator — its rate falling to zero is how an authenticated peer going quiet shows up. \"bad\" means a wrong or rotated key (or spoofing); \"make_failed\" is a local failure to generate a signature, i.e. a missing key in the kernel keytable; \"not_expected\" and \"not_provided\" are the two halves of an asymmetric configuration, where one side is signing and the other is not.--exporter.disable-protocol

Q-Feeds

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_qfeeds_feeds_totalGauge---Total number of configured Q-Feeds threat intelligence feeds--exporter.disable-qfeeds
opnsense_qfeeds_feed_entriesGaugefeedCurrent number of entries in this Q-Feeds feed--exporter.disable-qfeeds
opnsense_qfeeds_feed_packets_blocked_totalCounterfeedPackets blocked by this Q-Feeds feed since last reset--exporter.disable-qfeeds
opnsense_qfeeds_feed_bytes_blocked_totalCounterfeedBytes blocked by this Q-Feeds feed since last reset--exporter.disable-qfeeds
opnsense_qfeeds_feed_addresses_blockedGaugefeedCurrent number of addresses blocked by this Q-Feeds feed--exporter.disable-qfeeds
opnsense_qfeeds_feed_last_update_timestamp_secondsGaugefeedUnix timestamp of the last update for this Q-Feeds feed--exporter.disable-qfeeds
opnsense_qfeeds_feed_next_update_timestamp_secondsGaugefeedUnix timestamp of the next scheduled update for this Q-Feeds feed--exporter.disable-qfeeds
opnsense_qfeeds_entriesGauge---Total current entries across all Q-Feeds feeds--exporter.disable-qfeeds
opnsense_qfeeds_packets_blocked_totalCounter---Total packets blocked across all Q-Feeds feeds since last reset--exporter.disable-qfeeds
opnsense_qfeeds_bytes_blocked_totalCounter---Total bytes blocked across all Q-Feeds feeds since last reset--exporter.disable-qfeeds
opnsense_qfeeds_addresses_blockedGauge---Total current addresses blocked across all Q-Feeds feeds--exporter.disable-qfeeds
opnsense_qfeeds_license_infoGaugelicenseQ-Feeds license information (value is always 1; see labels)--exporter.disable-qfeeds
opnsense_qfeeds_license_expiry_timestamp_secondsGauge---Unix timestamp of the Q-Feeds license expiry--exporter.disable-qfeeds

Relayd Load Balancer

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_relayd_virtualserver_statusGaugename, typeRelayd virtual server (redirect/relay) status (1 = active, 0 = otherwise)--exporter.disable-relayd
opnsense_relayd_table_activeGaugevirtualserver, tableRelayd table status (1 = active with at least one host, 0 = otherwise)--exporter.disable-relayd
opnsense_relayd_host_upGaugevirtualserver, table, host, addressRelayd table host health check status (1 = up, 0 = otherwise)--exporter.disable-relayd
opnsense_relayd_host_availability_percentGaugevirtualserver, table, host, addressRelayd table host availability percentage from health checks (omitted when no checks have run yet)--exporter.disable-relayd

SMART Disk Health

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_smart_devices_totalGauge---Number of SMART-monitored devices enumerated by the os-smart plugin--exporter.enable-smart
opnsense_smart_device_healthGaugedevice, model, serialSMART overall health assessment (1 = passed, 0 = failed)--exporter.enable-smart
opnsense_smart_device_temperature_celsiusGaugedeviceCurrent drive temperature in degrees Celsius--exporter.enable-smart
opnsense_smart_device_power_on_hoursGaugedeviceTotal power-on hours reported by the drive--exporter.enable-smart
opnsense_smart_attribute_valueGaugedevice, attribute_name, attribute_idNormalised current value of a SATA SMART attribute--exporter.enable-smart
opnsense_smart_attribute_worstGaugedevice, attribute_name, attribute_idWorst recorded normalised value of a SATA SMART attribute--exporter.enable-smart
opnsense_smart_attribute_thresholdGaugedevice, attribute_name, attribute_idFailure threshold of a SATA SMART attribute (normalised value at/below this indicates failure)--exporter.enable-smart
opnsense_smart_attribute_rawGaugedevice, attribute_name, attribute_idRaw value of a SATA SMART attribute (e.g. reallocated sector count, total LBAs written)--exporter.enable-smart
opnsense_smart_nvme_available_spare_percentGaugedeviceNVMe remaining spare capacity as a percentage--exporter.enable-smart
opnsense_smart_nvme_percentage_usedGaugedeviceNVMe vendor estimate of device life used as a percentage (may exceed 100)--exporter.enable-smart
opnsense_smart_nvme_media_errors_totalCounterdeviceNVMe count of unrecovered data-integrity errors--exporter.enable-smart
opnsense_smart_nvme_unsafe_shutdowns_totalCounterdeviceNVMe count of unsafe shutdowns--exporter.enable-smart
opnsense_smart_nvme_data_units_read_totalCounterdeviceNVMe data units read (1 unit = 1000 × 512 bytes)--exporter.enable-smart
opnsense_smart_nvme_data_units_written_totalCounterdeviceNVMe data units written (1 unit = 1000 × 512 bytes)--exporter.enable-smart
opnsense_smart_device_rotation_rate_rpmGaugedeviceDrive rotation speed in RPM as reported by the drive itself. 0 explicitly means solid-state (no spinning platter); any other value is the platter's actual RPM. Use this to pick which wear/temperature thresholds apply — an ABSENT series means the drive didn't report this field at all, which is not the same as a genuine 0 (#577).--exporter.enable-smart
opnsense_smart_device_spare_available_percentGaugedeviceSSD spare/reserve blocks remaining, as a percentage of the original spare pool. smartctl derives this by matching vendor-specific reallocated-sector/spare-block attributes, so it is only reported for drives it can normalize — falling toward the drive's own threshold means the wear-leveling reserve is running out (#577).--exporter.enable-smart
opnsense_smart_device_endurance_used_percentGaugedeviceSSD endurance used, normalized by smartctl from vendor-specific wear-leveling attributes (0-100+; values above 100 mean the drive has exceeded its rated write endurance and failure risk keeps rising). Only reported for drives smartctl can normalize this from (#577).--exporter.enable-smart
opnsense_smart_attribute_failedGaugedevice, attribute_name, attribute_id, when_failedEmitted with value 1 ONLY for a SATA SMART attribute whose own when_failed marker is non-empty — i.e. that specific attribute, not just the drive's overall smart_status, has failed its threshold now or in the past. when_failed carries the raw smartctl value (\"now\" or \"past\"). A healthy attribute emits no series at all (absence means \"never failed\", not \"unknown\"), so a clean fleet adds ~0 cardinality. This is deliberately a separate metric rather than a new label on attribute_value/worst/threshold/raw: adding a label to those would change their series identity and break continuity of every existing panel/rule reading them (#577).--exporter.enable-smart
opnsense_smart_device_info_errorsGaugereasonDevices whose smartInfo payload could not be fully read in the last poll, by reason (failed = nothing decoded, partial = schema disagreement)--exporter.enable-smart

Services

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_services_statusGaugename, descriptionService status by name and description (1 = running, 0 = stopped)--exporter.disable-services
opnsense_services_running_totalGauge---Total number of running services--exporter.disable-services
opnsense_services_stopped_totalGauge---Total number of stopped services--exporter.disable-services

Siproxd

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_siproxd_registrationsGauge---Number of active SIP registrations tracked by siproxd. Silent when the os-siproxd plugin is absent.--exporter.disable-siproxd

Syslog

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_syslog_processed_totalCountersource_name, source_id, source_instanceMessages processed by this syslog-ng object since stats reset--exporter.disable-syslog
opnsense_syslog_dropped_totalCountersource_name, source_id, source_instanceMessages dropped by this syslog-ng object since stats reset--exporter.disable-syslog
opnsense_syslog_written_totalCountersource_name, source_id, source_instanceMessages written by this syslog-ng object since stats reset--exporter.disable-syslog
opnsense_syslog_queuedGaugesource_name, source_id, source_instanceMessages currently queued in this syslog-ng object--exporter.disable-syslog
opnsense_syslog_truncated_messages_totalCountersource_name, source_id, source_instanceMessages truncated by this syslog-ng object since stats reset--exporter.disable-syslog
opnsense_syslog_truncated_bytes_totalCountersource_name, source_id, source_instanceBytes truncated by this syslog-ng object since stats reset--exporter.disable-syslog
opnsense_syslog_memory_usage_bytesGaugesource_name, source_id, source_instanceCurrent memory usage of this syslog-ng object in bytes--exporter.disable-syslog
opnsense_syslog_events_per_secondGaugesource_name, source_id, source_instance, windowsyslog-ng events per second over the labelled window (1h, 24h, since_start)--exporter.disable-syslog
opnsense_syslog_message_size_bytesGaugesource_name, source_id, source_instance, statsyslog-ng message size in bytes (stat = avg or max)--exporter.disable-syslog
opnsense_syslog_target_stateGaugesource_name, source_id, source_instance, stateCurrent lifecycle state of a syslog-ng source/target object (always 1; one series per SourceName/SourceId/SourceInstance). state is drawn from syslog-ng's closed state vocabulary -- active (currently alive and receiving stat updates), dynamic (a runtime-created object that may cease to exist), orphaned (the underlying config element was removed but its last-known counters are retained) -- and anything unrecognized collapses to unknown. A target's byte/message counters going flat while this reads orphaned (rather than active) is exactly the stall this metric exists to distinguish from an idle-but-fine target.--exporter.disable-syslog
opnsense_syslog_service_runningGauge---Whether the syslog-ng service is running (1 = running, 0 = stopped/disabled)--exporter.disable-syslog

System

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_system_memory_total_bytesGauge---Total physical memory in bytes--exporter.disable-system
opnsense_system_memory_used_bytesGauge---Used physical memory in bytes--exporter.disable-system
opnsense_system_memory_arc_bytesGauge---ZFS ARC memory usage in bytes--exporter.disable-system
opnsense_system_uptime_secondsGauge---System uptime in seconds--exporter.disable-system
opnsense_system_boot_timestamp_secondsGauge---Unix timestamp at which the firewall booted, taken from the API's own boottime value rather than derived from uptime. Anchors the reboot dashboard annotation (#421): a query-time time()-uptime is recomputed on every evaluation and drifts between them, which moves the marker. Absent when the systemTime sub-call failed or boottime was unparseable.--exporter.disable-system
opnsense_system_load_averageGaugeintervalSystem load average--exporter.disable-system
opnsense_system_config_last_changeGauge---Unix timestamp of last configuration change--exporter.disable-system
opnsense_system_disk_total_bytesGaugedevice, type, mountpointTotal disk space in bytes--exporter.disable-system
opnsense_system_disk_used_bytesGaugedevice, type, mountpointUsed disk space in bytes--exporter.disable-system
opnsense_system_disk_usage_ratioGaugedevice, type, mountpointDisk usage as a ratio from 0.0 to 1.0--exporter.disable-system
opnsense_system_swap_total_bytesGaugedeviceTotal swap space in bytes--exporter.disable-system
opnsense_system_swap_used_bytesGaugedeviceUsed swap space in bytes--exporter.disable-system
opnsense_system_infoGaugehostname, opnsense_version, freebsd_version, openssl_version, cpu_model, cpu_cores, cpu_threadsSystem information with hostname, OS versions, and CPU details (value is always 1)--exporter.disable-system

Tailscale

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_tailscale_service_runningGauge---Whether the Tailscale plugin service is running (1 = running, 0 = stopped/disabled)--exporter.disable-tailscale
opnsense_tailscale_backend_runningGauge---Whether the tailscaled backend state is Running (1 = Running, 0 = anything else)--exporter.disable-tailscale
opnsense_tailscale_infoGaugeversion, relayTailscale node information (value is always 1; see labels)--exporter.disable-tailscale
opnsense_tailscale_peers_totalGauge---Number of tailnet peers known to this node--exporter.disable-tailscale
opnsense_tailscale_peers_with_active_sessionGauge---Number of tailnet peers with an established WireGuard session from this node (derived from local last-handshake presence, not coordination-server online state)--exporter.disable-tailscale
opnsense_tailscale_health_warningsGauge---Number of live health warning strings reported by the local tailscaled client (e.g. update available, DERP unreachable, key expiry). The warning text itself is never exported as a label.--exporter.disable-tailscale
opnsense_tailscale_peer_session_activeGaugepeerWhether this node has an established WireGuard session with the peer (1 = a handshake has been recorded since tailscaled start). Node-local; deliberately not the coordination-server online flag. Only emitted when --exporter.enable-tailscale-peer-details is set.--exporter.disable-tailscale
opnsense_tailscale_peer_directGaugepeerWhether the established session to the peer uses a direct (non-relayed) path (1 = direct, 0 = DERP-relayed). Only emitted for peers with a WireGuard session and when --exporter.enable-tailscale-peer-details is set.--exporter.disable-tailscale
opnsense_tailscale_peer_rx_bytes_totalCounterpeerBytes received from this peer by this node since tailscaled start. Only emitted when --exporter.enable-tailscale-peer-details is set.--exporter.disable-tailscale
opnsense_tailscale_peer_tx_bytes_totalCounterpeerBytes sent to this peer by this node since tailscaled start. Only emitted when --exporter.enable-tailscale-peer-details is set.--exporter.disable-tailscale
opnsense_tailscale_peer_last_handshake_timestamp_secondsGaugepeerUnix timestamp of the last WireGuard handshake with this peer from this node. Only emitted when --exporter.enable-tailscale-peer-details is set.--exporter.disable-tailscale
opnsense_tailscale_reauth_requiredGauge---Whether tailscaled is parked waiting for an interactive login (1 = it is holding an auth URL). The tunnel stays down until a human completes the login - no restart or retry clears it. Distinct from backend_running, which reads 0 for this and every other reason. The auth URL itself is a credential and is never exported, in a label or anywhere else.--exporter.disable-tailscale
opnsense_tailscale_key_expiry_timestamp_secondsGauge---Unix timestamp at which THIS node's own Tailscale key expires. When it does the tunnel dies with no other warning, so this is the direct analogue of a certificate expiry gauge. Not emitted at all when the node key does not expire - upstream omits the field entirely then, and a 0 would read as \"expired in 1970\" and page forever on a healthy node. Self only: peer key expiry is fleet inventory covered by tailscale2otel.--exporter.disable-tailscale

Temperature

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_temperature_celsiusGaugedevice, type, device_seqTemperature reading in Celsius--exporter.disable-temperature

Tor

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_tor_control_port_upGauge---Whether the Tor control port answered the last circuit and stream query (1 = up, 0 = unreachable/misconfigured)--exporter.enable-tor
opnsense_tor_circuitsGaugestatusNumber of Tor circuits by build status (unrecognised status values are bucketed as \"other\")--exporter.enable-tor
opnsense_tor_circuits_by_purposeGaugepurposeNumber of Tor circuits by purpose (unrecognised purpose values are bucketed as \"other\")--exporter.enable-tor
opnsense_tor_streamsGaugestatusNumber of Tor streams by status (unrecognised status values are bucketed as \"other\"); never labeled by destination--exporter.enable-tor
opnsense_tor_hidden_servicesGauge---Number of configured Tor hidden services--exporter.enable-tor
opnsense_tor_hidden_service_hostname_availableGaugeserviceWhether a .onion hostname has been provisioned for a configured hidden service (1 = available, 0 = not available); the hostname itself is never exposed--exporter.enable-tor

Traffic Shaper

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_trafficshaper_pipes_totalGauge---Total number of configured traffic shaper pipes--exporter.disable-trafficshaper
opnsense_trafficshaper_queues_totalGauge---Total number of configured traffic shaper queues (excluding template queues)--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_active_flowsGaugepipe, descriptionNumber of currently active flows on this pipe--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_packetsGaugepipe, descriptionTotal packets processed by this pipe (gauge: flow stats expire with idle flows)--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_bytesGaugepipe, descriptionTotal bytes processed by this pipe (gauge: flow stats expire with idle flows)--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_drop_packetsGaugepipe, descriptionTotal packets dropped by this pipe (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_drop_bytesGaugepipe, descriptionTotal bytes dropped by this pipe (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_active_flowsGaugequeue, pipe, descriptionNumber of currently active flows on this queue--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_packetsGaugequeue, pipe, descriptionTotal packets processed by this queue (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_bytesGaugequeue, pipe, descriptionTotal bytes processed by this queue (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_drop_packetsGaugequeue, pipe, descriptionTotal packets dropped by this queue (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_drop_bytesGaugequeue, pipe, descriptionTotal bytes dropped by this queue (gauge)--exporter.disable-trafficshaper
opnsense_trafficshaper_rule_packets_totalCounterrule, attached_to, target_type, descriptionCumulative packets matched by this traffic shaper rule--exporter.disable-trafficshaper
opnsense_trafficshaper_rule_bytes_totalCounterrule, attached_to, target_type, descriptionCumulative bytes matched by this traffic shaper rule--exporter.disable-trafficshaper
opnsense_trafficshaper_rule_last_match_timestamp_secondsGaugerule, attached_to, target_type, descriptionUnix timestamp of the last time this traffic shaper rule matched traffic. Absent for a rule that has never matched.--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_configured_bandwidth_bpsGaugepipe, descriptionConfigured bandwidth limit for this pipe, normalized to bits per second. Not emitted for a pipe with no bandwidth cap configured (dnctl reports \"unlimited\", not 0 bps).--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_configured_burst_bytesGaugepipe, descriptionConfigured burst allowance for this pipe in bytes.--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_configured_delay_millisecondsGaugepipe, descriptionConfigured added delay for this pipe in milliseconds. 0 is a real \"no added delay\" configuration, not an absence.--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_configured_queue_sizeGaugepipe, description, unitFolded from this pipe's own auto-attached (template) queue.--exporter.disable-trafficshaper
opnsense_trafficshaper_pipe_configured_weightGaugepipe, descriptionConfigured WF2Q+ scheduling weight of this pipe's own auto-attached (template) queue. A relative value with meaning only alongside sibling weights on the same scheduler.--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_configured_queue_sizeGaugequeue, pipe, description, unit--exporter.disable-trafficshaper
opnsense_trafficshaper_queue_configured_weightGaugequeue, pipe, descriptionConfigured WF2Q+ scheduling weight of this queue. A relative value with meaning only alongside sibling weights on the same scheduler.--exporter.disable-trafficshaper

Unbound DNS

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_unbound_dns_uptime_secondsGauge---Uptime of the unbound DNS service in seconds--exporter.disable-unbound
opnsense_unbound_dns_queries_totalCounter---Total number of queries received--exporter.disable-unbound
opnsense_unbound_dns_cache_hits_totalCounter---Total number of cache hits--exporter.disable-unbound
opnsense_unbound_dns_cache_miss_totalCounter---Total number of cache misses--exporter.disable-unbound
opnsense_unbound_dns_prefetch_totalCounter---Total number of cache prefetches--exporter.disable-unbound
opnsense_unbound_dns_expired_totalCounter---Total number of expired entries served--exporter.disable-unbound
opnsense_unbound_dns_recursive_replies_totalCounter---Total number of recursive replies sent--exporter.disable-unbound
opnsense_unbound_dns_queries_timed_out_totalCounter---Total number of queries that timed out--exporter.disable-unbound
opnsense_unbound_dns_queries_ip_ratelimited_totalCounter---Total number of queries that were IP rate limited--exporter.disable-unbound
opnsense_unbound_dns_answers_secure_totalCounter---Total number of DNSSEC secure answers--exporter.disable-unbound
opnsense_unbound_dns_answers_bogus_totalCounter---Total number of DNSSEC bogus answers--exporter.disable-unbound
opnsense_unbound_dns_rrset_bogus_totalCounter---Total number of DNSSEC bogus rrsets--exporter.disable-unbound
opnsense_unbound_dns_queries_discard_timeout_totalCounter---Total number of queries discarded after timing out waiting for a reply--exporter.disable-unbound
opnsense_unbound_dns_queries_wait_limit_totalCounter---Total number of queries dropped because the per-IP wait limit was reached--exporter.disable-unbound
opnsense_unbound_dns_queries_replyaddr_limit_totalCounter---Total number of queries dropped because the per-reply-address rate limit was reached--exporter.disable-unbound
opnsense_unbound_dns_dns_error_reports_totalCounter---Total number of RFC 9567 DNS error reports generated--exporter.disable-unbound
opnsense_unbound_dns_queries_by_type_totalCountertypeTotal queries by DNS record type--exporter.disable-unbound
opnsense_unbound_dns_queries_by_protocol_totalCounterprotocolTotal queries by protocol--exporter.disable-unbound
opnsense_unbound_dns_answers_by_rcode_totalCounterrcodeTotal answers by response code--exporter.disable-unbound
opnsense_unbound_dns_unwanted_totalCountertypeTotal number of unwanted queries or replies--exporter.disable-unbound
opnsense_unbound_dns_query_flags_totalCounterflagTotal queries by DNS flag--exporter.disable-unbound
opnsense_unbound_dns_edns_totalCountertypeTotal EDNS queries by type--exporter.disable-unbound
opnsense_unbound_dns_request_list_avgGauge---Average number of requests in the internal request list--exporter.disable-unbound
opnsense_unbound_dns_request_list_maxGauge---Maximum number of requests in the internal request list--exporter.disable-unbound
opnsense_unbound_dns_recursion_time_avg_secondsGauge---Average recursion time in seconds--exporter.disable-unbound
opnsense_unbound_dns_recursion_time_median_secondsGauge---Median recursion time in seconds--exporter.disable-unbound
opnsense_unbound_dns_cache_countGaugecacheNumber of entries in cache by cache type--exporter.disable-unbound
opnsense_unbound_dns_memory_bytesGaugecomponentMemory usage in bytes by component--exporter.disable-unbound
opnsense_unbound_dns_request_list_currentGaugescopeCurrent number of requests in the internal request list by scope--exporter.disable-unbound
opnsense_unbound_dns_request_list_overwritten_totalCounter---Total number of request list entries overwritten by newer entries--exporter.disable-unbound
opnsense_unbound_dns_request_list_exceeded_totalCounter---Total number of request list entries that exceeded the maximum--exporter.disable-unbound
opnsense_unbound_dns_tcp_usage_ratioGauge---TCP connection usage ratio for the DNS resolver (0.0 to 1.0)--exporter.disable-unbound
opnsense_unbound_dns_blocklist_enabledGauge---Whether the DNS blocklist is enabled (1 = enabled, 0 = disabled)--exporter.disable-unbound
opnsense_unbound_dns_service_runningGauge---Whether the service is running (1 = running, 0 = stopped/disabled)--exporter.disable-unbound
opnsense_unbound_dns_recursion_time_secondsGauge---How long Unbound's recursive lookups took, bucketed - the p50/p99 companion to the recursion_time_avg_seconds and recursion_time_median_seconds gauges. Buckets are unbound's own: exponential, doubling from 1us to 2^19s. Cumulative since the resolver started, so query it through rate(). ONLY EXISTS WHEN THE RESOLVER RUNS WITH extended-statistics: yes, which is OFF by default from OPNsense 26.7 - unbound does not compute the histogram's output at all otherwise, and the exporter emits no series rather than a fabricated empty one. The _sum is reconstructed by multiplying unbound's reported mean back by this histogram's own count, which is exact to the microsecond that mean is printed at; unbound never publishes the accumulated total directly.--exporter.disable-unbound
opnsense_unbound_dns_validation_operations_totalCounter---RRSIG verification operations the DNSSEC validator has attempted, counted whether they passed or failed (unbound's num.valops). This is the denominator answers_secure_total and answers_bogus_total lack: a validator doing heavy work for few secure answers is a different problem from one doing none at all, and the two are indistinguishable without it. Extended-statistics sourced, so absent when the box runs extended-statistics: no (the OPNsense 26.7 default).--exporter.disable-unbound
opnsense_unbound_dns_infra_rtt_secondsGaugeip, hostSmoothed round-trip time to an upstream server in Unbound's infra cache. Only emitted when --exporter.enable-unbound-infra is set.--exporter.disable-unbound
opnsense_unbound_dns_infra_rto_secondsGaugeip, hostRetransmission timeout for an upstream server in Unbound's infra cache. Only emitted when --exporter.enable-unbound-infra is set.--exporter.disable-unbound
opnsense_unbound_dns_infra_host_lameGaugeip, host, kindWhether Unbound has marked an upstream server lame - not authoritative for the zone it was asked about - by lameness kind (1 = lame, 0 = fine). kind=recursion means the server answers by recursing on our behalf instead of serving the zone; kind=type_a and kind=other split lameness by query type, because a server can serve A records fine and be lame for everything else. THIS IS THE SIGNAL RTT CANNOT CARRY: Unbound stops querying a lame server, so its RTT stays healthy while resolution through it fails. Only emitted when --exporter.enable-unbound-infra is set.--exporter.disable-unbound
opnsense_unbound_dns_infra_host_dnssec_lameGaugeip, hostWhether Unbound has marked an upstream server DNSSEC-lame (1 = lame, 0 = fine): it answers the zone but will not serve the DNSSEC records needed to validate those answers. Tracked separately from infra_host_lame because Unbound tracks it separately and it fails differently - queries succeed, validation does not. Only emitted when --exporter.enable-unbound-infra is set.--exporter.disable-unbound
opnsense_unbound_dns_infra_host_edns_brokenGaugeip, hostWhether Unbound has determined that EDNS queries or replies are being dropped in transit to an upstream server (1 = broken, 0 = fine), from its cached EDNS version for that host. Distinct from lameness: the server answers, but every EDNS-bearing exchange with it is eaten somewhere on the path, which silently disables DNSSEC and forces fallbacks. Usually a middlebox or MTU problem rather than the server itself. Only emitted when --exporter.enable-unbound-infra is set.--exporter.disable-unbound
opnsense_unbound_dns_qstats_enabledGauge---Whether Unbound query-stats logging (general.stats) is on (1 = enabled, 0 = disabled). Only emitted when --exporter.enable-unbound-qstats is set.--exporter.disable-unbound
opnsense_unbound_dns_dnsbl_blocklist_sizeGauge---Number of entries in the currently loaded DNSBL blocklist. Gauge: reflects whatever list is loaded right now. Only emitted when --exporter.enable-unbound-qstats is set and query-stats logging is on.--exporter.disable-unbound
opnsense_unbound_dns_qstats_queries_7dGaugeresultDNSBL query-stats outcome totals over Unbound's rolling query-stats window (typically the last 7 days), by result. Gauge, not a counter: the underlying window is truncated hourly and can decrease. Only emitted when --exporter.enable-unbound-qstats is set and query-stats logging is on.--exporter.disable-unbound
opnsense_unbound_dns_qstats_queries_total_7dGauge---Total DNS queries over Unbound's rolling query-stats window. Gauge, not a counter, for the same reason as qstats_queries_7d. Only emitted when --exporter.enable-unbound-qstats is set and query-stats logging is on.--exporter.disable-unbound
opnsense_unbound_dns_qstats_start_time_secondsGauge---Unix timestamp the current query-stats rolling window starts from. A jump forward beyond the expected daily roll-off signals the underlying qstats database was reset. Only emitted when --exporter.enable-unbound-qstats is set and query-stats logging is on.--exporter.disable-unbound
opnsense_unbound_dns_local_zonesGaugetypeNumber of configured Unbound local zones, by zone type. Only emitted when --exporter.enable-unbound-qstats is set.--exporter.disable-unbound
opnsense_unbound_dns_local_data_recordsGauge---Total number of configured Unbound local-data resource records. Only emitted when --exporter.enable-unbound-qstats is set.--exporter.disable-unbound
opnsense_unbound_dns_insecure_domainsGauge---Number of domains configured as DNSSEC-insecure in Unbound. Only emitted when --exporter.enable-unbound-qstats is set.--exporter.disable-unbound
opnsense_unbound_dns_qstats_top_domain_queries_7dGaugedomain, resultQueries for one domain over Unbound's rolling query-stats window (typically 7 days), for the busiest domains only, split by outcome: result=passed is the top-N of queries Unbound answered, result=blocked the top-N a DNSBL policy blocked - the pi-hole-style leaderboard. A domain can appear under both, since some queries for it may predate the policy that now blocks it. Gauge, not a counter: the window is truncated hourly and a qstats reset empties it, so these totals decrease. BOUNDED ON BOTH AXES and truncated by design - the API is asked for at most 512 rows per outcome, at most 512 domains are tracked per outcome, and a domain is retired 5 minutes after it last appeared. This is the busiest domains, never all of them, and summing it does not reconstruct qstats_queries_7d. Domains carry unbound's trailing root dot. Refusals past the cap surface as cardinality_capped_total; a rising refusal count means something is minting domains faster than the cap allows (random-subdomain or DNS-tunnelling traffic does exactly this) and the leaderboard below it is no longer the real top-N. Only emitted when --exporter.enable-unbound-qstats is set and query-stats logging is on.--exporter.disable-unbound
opnsense_unbound_dns_cardinality_capped_totalCounterfamilyDomains refused their own series because the leaderboard already held its full key budget when they were first seen. Non-zero and rising means the top-N is saturated and no longer reflects the real busiest domains - the usual cause is random-subdomain or DNS-tunnelling traffic churning the leaderboard, which is worth looking at in its own right. There is deliberately no companion gauge for the live key count: count() over qstats_top_domain_queries_7d derives it exactly.--exporter.disable-unbound

Vnstat Traffic Accounting

Metric NameTypeLabelsDescriptionEnable Flag
opnsense_vnstat_bytes_totalCounterinterface, directionCumulative bytes recorded by vnstat since this interface's database was created (counter; resets only on vnstat --resetdb)--exporter.enable-vnstat
opnsense_vnstat_current_day_bytesGaugeinterface, directionBytes recorded by vnstat so far today (gauge; resets to 0 at local midnight in vnstat's own bookkeeping)--exporter.enable-vnstat
opnsense_vnstat_current_month_bytesGaugeinterface, directionBytes recorded by vnstat so far this month (gauge; resets monthly)--exporter.enable-vnstat

Wireguard

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_wireguard_interfaces_statusGaugedevice, device_type, device_nameWireguard interface (1 = up, 0 = down)--exporter.disable-wireguard
opnsense_wireguard_peer_statusGaugedevice, device_type, device_name, peer_nameWireguard peer status (1 = up, 0 = down, 2 = unknown, 3 = stale)--exporter.disable-wireguard
opnsense_wireguard_peer_received_bytes_totalCounterdevice, device_type, device_name, peer_nameBytes received by this wireguard peer--exporter.disable-wireguard
opnsense_wireguard_peer_transmitted_bytes_totalCounterdevice, device_type, device_name, peer_nameBytes transmitted by this wireguard peer--exporter.disable-wireguard
opnsense_wireguard_peer_last_handshake_secondsGaugedevice, device_type, device_name, peer_nameLast handshake by peer in seconds--exporter.disable-wireguard
opnsense_wireguard_peer_handshake_age_secondsGaugedevice, device_type, device_name, peer_nameSeconds since the peer's last handshake--exporter.disable-wireguard
opnsense_wireguard_service_runningGauge---Whether the service is running (1 = running, 0 = stopped/disabled)--exporter.disable-wireguard

ZFS Boot Environments

Metric NameTypeLabelsDescriptionDisable Flag
opnsense_snapshots_supportedGauge---Whether the root filesystem supports ZFS boot environments (1 = ZFS/bectl, 0 = e.g. UFS).--exporter.disable-snapshots
opnsense_snapshots_totalGauge---Number of ZFS boot environments currently present.--exporter.disable-snapshots
opnsense_snapshots_active_created_timestamp_secondsGauge---Unix timestamp when the currently active boot environment (active flag containing \"N\") was created. Absent when no boot environment is marked active (e.g. unsupported filesystem).--exporter.disable-snapshots