Upgrading¶
This page lists breaking changes by release, most recent first, plus notes for users migrating from the upstream AthennaMind exporter. Full details for every release: Changelog.
Upgrading to v4.0 from v3.x¶
-
Six redundant Zenarmor structured-metadata keys are no longer shipped -
organization,policyid,src_geoip.latitude,src_geoip.longitude,dst_geoip.latitudeanddst_geoip.longitudeare no longer attributed onto each record, and the OTLP instrumentation scope name is now empty so Loki stops addingscope_name. Structured metadata is billed as ingested volume, and these measured ~231 bytes per line of pure repetition on the largest log source. Every one of them is still in the log body, which is unchanged, so a line-level lookup still has them; only the re-extracted copy is gone. A LogQL filter using| organization=,| policyid=,| scope_name=or either coordinate pair will stop matching - drop the filter, or read the value out of the body with| json. Nothing in the bundled dashboard, alert rules or exported metrics consumed any of them.--logs.zenarmor.excluderules namingorganizationorpolicyidnow fail at startup as unknown fields; such a rule was always either a no-op or a total drop, because both keys carry exactly one value per deployment. -
opnsense_exporter_otlp_*now carries theopnsense_instancelabel - the four OTLP delivery-health series (otlp_enabled,otlp_exports_total{result},otlp_consecutive_failures,otlp_last_success_timestamp_seconds) were registered without appliance identity, so on a multi-firewall stack there was no way to tell whose push pipeline had stalled. They now carryopnsense_instancelike every otheropnsense_exporter_*family. A recording rule, alert or dashboard query that aggregates these series withoutby (opnsense_instance)will now return one series per exporter instead of one overall - add the grouping, or usesum without (opnsense_instance) (...)to keep the old shape. Single-instance deployments see no change beyond the extra label. The bundled dashboard and rules are already updated. -
Scrape deadline compatibility surfaces removed - the
exporter.scrape-timeout-offsetoption (formerly passed with two leading hyphens),opnsense_exporter_scrape_skips_total, and the exporter's handling ofX-Prometheus-Scrape-Timeout-Secondshave been removed. They became inert when collection moved to the background poll scheduler:/metricsnow replays an in-memory snapshot and does not make OPNsense API calls. Remove the flag from deployment arguments and replace skip-counter alerts with scheduler freshness, duration, and failure signals. Prometheus's ownscrape_timeoutstill bounds the HTTP request. -
Log freshness gauges have stage-specific names -
opnsense_exporter_logs_last_event_timestamp_secondshas been removed. Useopnsense_exporter_logs_last_received_timestamp_seconds{source}to measure when the exporter last admitted input andopnsense_exporter_logs_last_exported_timestamp_seconds{source}to measure when the sink last acknowledged delivery. Both use the exporter clock; neither uses a sender-controlled event timestamp. -
Container health uses the native
healthsubcommand - the distroless image now runsopnsense-exporter health, which probes/-/healthywithout requiring a shell,curl, orwget. Custom images and Compose overrides that copied the oldwgethealthcheck should switch to this command. Docker health status alone does not triggerrestart:; process exit or an external unhealthy-container remediation mechanism is still required for automatic replacement. -
VLAN traffic on a trunk is now attributed by subnet, so interface labels move - a NetFlow record captured on a trunk whose address falls inside exactly one VLAN child's configured subnet is now attributed to that child immediately, instead of waiting up to two seconds for the child's own copy to arrive and win. Nothing is dropped and no metric is renamed, but per-interface flow volume shifts off the trunk and onto the VLAN children, which is the correction: a production measurement found 29.2% of trunk/child pairs had a gap wider than the two-second window, and every one of those flows was being counted against the trunk. A further 247,105 trunk-captured records over 18h35m had no child copy at all and could never have been attributed by timing. If you have dashboards or alerts with a hard-coded trunk interface name, or thresholds calibrated on trunk volume, expect both to change on upgrade. Two new counters make it observable:
opnsense_flow_vlan_subnet_attributed_totalcounts the attributions andopnsense_flow_vlan_late_child_copies_totalcounts the residual that arrived too late to correct. The two-second hold remains for addresses that match no child subnet or several of them, so a VLAN with no configured subnet behaves exactly as before. -
PF packet counters renamed with a
_totalsuffix - the eightopnsense_firewall_{in,out}_{ipv4,ipv6}_{pass,block}_packetsseries are now..._packets_total. They were always emitted as Prometheus counters, but the unsuffixed names only ever appeared on a direct/metricsscrape: OTLP-to-Prometheus canonicalization appends_totalto every monotonic sum, so a stack fed through the OTLP bridge exposed only the_totalnames and every panel, recording rule, and alert written against the unsuffixed name silently returned no data. The descriptors now match what both paths expose. The sibling byte counters already carried_totaland are unchanged. Update any custom dashboard, recording rule, or alert that referenced the unsuffixed names; the bundled Grafana dashboard and rules are already updated. The generated metric catalogue also now types all sixteen pf pass/block series as Counter -
the eight packet series were previously mis-documented as Gauge.
-
IPsec and vnStat counters renamed with a
_totalsuffix - the eightopnsense_ipsec_phase{1,2}_{bytes,packets}_{in,out}series are now..._{in,out}_total(for exampleopnsense_ipsec_phase1_bytes_inis nowopnsense_ipsec_phase1_bytes_in_total), andopnsense_vnstat_total_bytesis nowopnsense_vnstat_bytes_total. All nine were always emitted as Prometheus counters, and OTLP-to-Prometheus canonicalization appends_totalto every monotonic sum regardless of the Go-declared name, so the unsuffixed names would disagree with what the supported OTLP-fed live backend exports as soon as the series is populated (an IPsec tunnel exists, or vnStat is enabled) - the same convention violation #418 fixed for the firewall pf pass/block descriptors, closed here before it could bite in production. Update any custom dashboard, recording rule, or alert that referenced the unsuffixed names; the bundled Grafana dashboard is already updated.vnstat_total_bytesbecomesvnstat_bytes_totalrather than a mechanical..._total_bytes_total: "total" here has always meant "cumulative since vnstat's database was created," never "rx+tx combined," and thedirectionlabel carrying rx/tx is untouched by this rename. -
Dashboard feature detection and log panels are now scoped to the selected instance - the hidden feature sentinels that drive conditional rendering, and every Loki panel, previously searched the whole datasource. In a multi-firewall stack that made navigation lie: selecting appliance A could expose a tab because appliance B had the feature, and raw-log and top-talker panels could show another firewall's records. Prometheus sentinels now filter
opnsense_instance, Loki queries filterservice_instance_id, and runtime metrics with no appliance label join toopnsense_up. Three visible consequences after regenerating the dashboard: tabs for features the selected appliance lacks now correctly disappear; the Traffic Shaper and Captive Portal tabs now appear when the feature is deployed but idle, where a zero-count test previously hid them; and the NetFlow receiver rows are now gated on the exporter's own receiver metric rather than OPNsense's netflow-export setting, so they hide on a box that exports netflow without this exporter receiving it.
Upgrading to v2.0 from v1.x¶
- SMART collector is now opt-in - the
opnsense_smart_*metrics are no longer emitted by default. Set--exporter.enable-smart(envOPNSENSE_EXPORTER_ENABLE_SMART=true) to restore them. Querying SMART data is one of the more expensive per-scrape calls, so it now has to be requested explicitly. - ARP/NDP per-entry series are opt-in - the per-entry
opnsense_arp_table_entriesandopnsense_ndp_entriesseries (one series per host, high cardinality) are no longer emitted by default. Set--exporter.enable-arp-details/--exporter.enable-ndp-detailsto restore them. Otherwise switch dashboards and alerts to the newopnsense_arp_table_entries_total/opnsense_ndp_entries_totalaggregate gauges, which are always emitted. opnsense_firewall_interface_hits_totalrenamed and re-typed - it is nowopnsense_firewall_interface_log_entries_recentand is a gauge, not a counter. It reflects the current count of recent log entries, so it no longer makes sense to wrap inrate()/increase()- plot the gauge directly. The bundled Grafana dashboard has already been updated.- Default instance label changed - when
--exporter.instance-labelis unset, theinstancelabel now defaults to the configured OPNsense address (deterministic across restarts) rather than the hostname reported by the API. To keep the old hostname-derived behaviour, set--exporter.instance-use-hostname; to pin an explicit value, set--exporter.instance-label. If you relied on the old default, existing series will change theirinstancelabel after the upgrade. - Portable Prometheus alert rules removed -
grafana/alerts/opnsense.rules.yamlno longer ships. If you were loading that file into Prometheus, Mimir, or the Grafana Cloud ruler, migrate to the Grafana-managed alert manifests undergrafana/alerts/grafana-managed/(pushed as Grafana resources). See Integration & Dashboards. - Unknown link state no longer reported as down - interfaces whose link state the API reports as unknown (e.g. some PPPoE WANs) are now distinguished from interfaces that are actually down instead of being flattened to down. Alerts that treated "not up" as "down" may fire differently; check any rules built on interface link-state metrics.
Upgrading to v1.0 from v0.x¶
- IPsec SPI labels removed - phase-2 metrics no longer carry
spi_in/spi_outlabels (remaining:description,name,phase1_name). SPIs rotate on every rekey, so the labels caused unbounded series churn. Update any PromQL that referenced them. - OpenVPN per-session metrics are opt-in - the per-session
opnsense_openvpn_sessionsseries (username and tunnel-address labels) is only emitted with--exporter.enable-openvpn-details. The aggregateopnsense_openvpn_sessions_totalandopnsense_openvpn_sessions_by_instanceseries are always emitted. Set the flag to restore the old behaviour. - WireGuard handshake metric type -
opnsense_wireguard_peer_last_handshake_secondschanged from counter to gauge (it is a Unix timestamp). Replacerate(opnsense_wireguard_peer_last_handshake_seconds[...])with the purpose-builtopnsense_wireguard_peer_handshake_age_secondsgauge. opnsense_upsemantics -opnsense_upno longer flips to 0 for a box that is reachable but self-reports as degraded (e.g. a leftover crash report). Such a box now trips the warning-levelOPNsenseCrashReports/OPNsenseFirewallUnhealthyalerts instead of the criticalOPNsenseExporterDown. If you alerted onopnsense_up == 0for these cases, switch to those signals.
Migrating from upstream (AthennaMind/opnsense-exporter)¶
In addition to the items above:
- Image and module path - pull
ghcr.io/rknightion/opnsense-exporter; the Go module isgithub.com/rknightion/opnsense-exporter. --runtime.gomaxprocsremoved - Go now auto-detects CPUs; delete the flag from any unit files or manifests./debug/pprof/*endpoints removed - replaced by optional authenticated push profiling via--pyroscope.*flags. See Configuration.- Firmware metrics reworked - version strings consolidated into
opnsense_firmware_info(labels) plus numeric gauges (needs_reboot,upgrade_needs_reboot,last_check_timestamp_seconds,new_packages_count,upgrade_packages_count). --exporter.instance-labelnow optional - when left empty it defaults to the configured OPNsense address (see the v2.0 note above for the change from the old hostname default; set--exporter.instance-use-hostnamefor hostname-derived labels).- Many new collectors are enabled by default - review the collector switches and disable what you don't need.