Troubleshooting¶
If nothing here matches what you are seeing, search the existing GitHub issues, ask in GitHub Discussions, or file a bug report with the exporter version, your OPNsense release, and the relevant log lines.
opnsense_up is 0¶
opnsense_up 0 means the exporter could not complete the OPNsense health check. Work through:
- Connectivity - can the exporter host reach the firewall?
curl -k https://<opnsense-address>/api/core/system/status -u "<key>:<secret>" - Credentials - a
401from the curl above means the API key/secret is wrong or the user is disabled. Regenerate the key pair under System > Access > Users > [user] > API keys. - Permissions - a
403means the API user lacks privileges. Grant the permissions listed in Security. - TLS - a certificate error means the firewall uses a self-signed or private-CA certificate. Either add the CA to the exporter's trust store (see Docker deployment) or, for testing only, set
--opnsense.insecure.
Run with --log.level=debug to see each API call and its failure reason.
A collector's metrics are missing¶
- Disabled? Check
opnsense_exporter_collector_enabled{collector="<name>"}-0means a--exporter.disable-*flag (or missing--exporter.enable-*flag for opt-in collectors) removed it. See Configuration. - Plugin absent? Plugin-backed collectors (ACME, SMART, DynDNS, ISC DHCPv4) stay silent when the OPNsense plugin is not installed - the API returns 404 and the exporter treats it as "feature absent" by design. Ask the exporter rather than guessing:
opnsense_feature_available{feature="<name>"}is1when the plugin answered and0when it returned 404, and theenabledlabel says whether that collector is switched on. No series at all means availability has never been determined - not that the plugin is missing. - Installed but not being scraped?
opnsense_feature_available{enabled="false"} == 1lists every plugin the box has that nothing is reading, which covers both an opt-in collector nobody turned on and a default-on one somebody disabled. The exporter also names each one, with the flag that would enable it, in afeature available but its collector is not enabledlog line - and aplugin inventoryline summarising the counts.--exporter.enable-all-availableturns on every collector whose plugin the startup probe found present. - Endpoint errors? Check
opnsense_exporter_endpoint_errors_totaland the exporter logs for the failing endpoint. - Unbound statistics empty? Enable Unbound DNS > Advanced > Extended Statistics on the firewall.
Push receivers: nothing arrives¶
Syslog, Zenarmor, and NetFlow are push lanes, so a quiet dashboard can mean that the sender never reached the exporter, the receiver rejected the input, or the OTLP sink has not acknowledged it. Start with the receiver self-metrics on /metrics:
opnsense_exporter_logs_shipped_total{source="syslog"}andopnsense_exporter_logs_shipped_total{source="zenarmor"}count records the sink confirmed as delivered. Flow-log records usesource="netflow"orsource="merged".opnsense_exporter_logs_rejected_total{source="<receiver>",reason="<reason>"}counts input refused before parsing. Thesourceissyslogorzenarmor;reasonidentifies the allowlist, authentication, body, filter, capacity, or endpoint path that refused it.opnsense_exporter_logs_parse_errors_total{source="<receiver>",stage="<stage>"}means the record arrived but lost structure while parsing; the raw record still ships. Syslog reportsstage="envelope"; Zenarmor reportsstage="bulk"orstage="document".- If input is arriving but acknowledgement is not, check
opnsense_exporter_logs_dropped_total{source="<source>",reason="<reason>"}andopnsense_exporter_logs_ship_errors_totalfor queue or destination failures.
Then follow the receiver-specific checklist:
- Syslog receiver - verify both UDP and TCP publication, the enabled OPNsense target, and the peer allowlist.
- Zenarmor receiver - verify the selected transport, its destination URI/port, and the matching receiver switch.
- NetFlow and flow volume - verify the OPNsense Reporting → NetFlow export target, UDP reachability, and template/decoder counters.
A replayed config revision is not in Grafana yet¶
configchange records retain the revision's original timestamp. A replay older than Loki's default three-hour query_ingesters_within window is read from the store, not the ingesters, and can remain absent until the chunk flushes. The default idle flush is 30 minutes and the maximum chunk age is two hours, so allow about two hours in practice. These are Loki defaults; the tenant may differ. See Loki's querier and ingester configuration.
If the retained timestamp is older than the default seven-day reject_old_samples_max_age limit, Loki rejects it when old-sample rejection is enabled. The exporter records the OTLP partial-success result in opnsense_exporter_logs_dropped_total{source="configchange",reason="rejected"}. The actual tenant limit may differ; this page does not assume one. See Loki limits_config.
Data is stale or collector polls are slow¶
Prometheus scrapes replay an in-memory snapshot; they do not fan out to OPNsense. With ~82 collectors polling on independent schedules, use the per-collector clocks to isolate stale data or a background collector missing its schedule:
- Check
opnsense_exporter_collector_snapshot_timestamp_secondsandopnsense_exporter_collector_last_success_timestamp_secondsfor retained-data age, thenopnsense_exporter_scrape_collector_duration_secondsfor the latest scheduled poll duration. Thescrape_prefix is retained for compatibility. - Check
opnsense_exporter_endpoint_errors_totaland per-endpoint request latency to identify the slow or failing OPNsense call. --exporter.max-scrape-durationis now the outer deadline for one background collector poll.--opnsense.timeout×--opnsense.max-retriesbounds an endpoint attempt sequence inside it; keep that product below the poll deadline.- Lower
--opnsense.max-concurrent-requeststo protect a low-power appliance, or raise it when independent polls are queuing behind the concurrency cap. - The
activitycollector runs atop(1)-equivalent snapshot and is commonly the slowest default-on poll. SMART performs onesmartctl -aPOST per disk on each scheduled poll and can wake spun-down disks; it remains opt-in.
Prometheus scrape_timeout still bounds the /metrics HTTP request, but changing it cannot change OPNsense API polling.
Too many time series¶
The detail flags (--exporter.enable-*-details) emit one series per DHCP lease, firewall rule, or VPN session and can produce thousands of series on busy networks. Leave them off unless you need per-item data, and review high-cardinality options.
What configuration is actually in force?¶
The exporter logs its entire resolved configuration at startup, at Info, one entry per section (effective config: Connection, effective config: Collectors, and so on). That is the resolved view - after file-based secrets are read and after --exporter.enable-all-available and its availability probe have been applied - so it is what the process is running, not what was typed. Credentials appear only as set or unset, never as values.
A plugin inventory line follows once the first availability probe completes. It is separate on purpose: availability needs the firewall to answer, and printing a confidently empty list while the box is unreachable would be worse than printing it a few seconds late.
The same content is available two other ways, rendered from the same source: the /config page of the operator console, and --config.check, which prints it and exits without contacting the firewall.
Where are the exporter's own logs?¶
The exporter logs to stdout (--log.format=logfmt|json, --log.level=debug|info|warn|error). In Docker/Kubernetes use docker logs / kubectl logs on the container.