OPNsenseXXXIV · Monitoring and Observability IntegrationGrafana dashboards
Grafana dashboards for OPNsense — from raw metrics to operator insight
What you'll learn
- Configure a Grafana data source pointing at the Prometheus instance
- Import the community OPNsense dashboard and adapt it to the deployment
- Identify the panels that an on-call operator must have at the top of the dashboard
- Write alert rules for state table occupancy, CPU saturation, and interface drops
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
Prometheus stores the metrics. Grafana turns them into pictures an operator can read at 03:00. The difference between a dashboard an operator uses and a dashboard an operator ignores is whether the panels answer the operator’s actual questions in the order the operator asks them. A dashboard with 60 panels and no prioritisation is decoration; a dashboard with 6 panels in the right order is a triage tool.
This lesson covers the Prometheus data source, the community OPNsense dashboards, the panels that belong at the top, and the alert rules that turn thresholds into pages.
The data source
Grafana does not store metrics — it queries Prometheus (or InfluxDB, Elasticsearch, Loki, and others). The first step is a data source:
- In Grafana: Connections → Data sources → Add data source → Prometheus.
- URL:
http://prometheus-server:9090(the Prometheus server’s address). - Authentication: none for internal Prometheus; basic auth or mTLS for external.
- Scrape interval: 30s (matches the Prometheus scrape).
- Click “Save & test” — confirm “Data source is working”.
The data source is the plumbing. Once it is in place, every dashboard panel that references the data source works.
The community dashboards
The Grafana community has published several OPNsense dashboards at grafana.com/grafana/dashboards. The most useful ones for an operator are:
- OPNsense — System Health (community ID varies by version): CPU, memory, state table, load, uptime.
- OPNsense — Interfaces: per-interface bandwidth, drops, errors.
- OPNsense — PF Statistics: state table over time, rule match counts.
- OPNsense — Suricata: alert counts, packet counts, drops.
Importing is straightforward: download the dashboard JSON from Grafana’s website, then in Grafana: Dashboards → New → Import → Upload JSON file. The dashboard will reference the Prometheus data source by name — usually the default Prometheus works.
The panels that matter
The panels an on-call operator must have at the top of the dashboard, in the order the operator will look at them during a triage:
| Position | Panel | Question it answers |
|---|---|---|
| 1 | State table occupancy (current value + trend) | Is the firewall refusing new flows? |
| 2 | CPU utilisation + interrupt share | Is the firewall CPU-bound? |
| 3 | Memory used + swap activity | Is memory pressure building? |
| 4 | Interface drops, errors, bps (per interface) | Is an interface saturating? |
| 5 | Recent firewall log volume (lines/min) | Is logging volume unusual? |
| 6 | CARP state per VHID | Is HA healthy? |
The order is deliberate. State table first because it is the canary metric from the previous lesson. CPU and memory next because they describe the firewall as a system. Interface counters because they describe the network. Logs because unusual volume is an early signal of an attack or a misconfiguration. CARP last because it is checked only when HA is involved.
A dashboard with 60 panels that buries these six in the middle is a dashboard nobody will read in an incident.
$ curl -s 'http://prometheus:9090/api/v1/query?query=opnsense_pf_states_total' | head -20{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"opnsense_pf_states_total","instance":"192.0.2.1:9100","job":"opnsense","device":"opnsense-fw-01","site":"dc1"},"value":[1723411200.123,"3127"]}]}}Illustrative output
Alert rules
A dashboard without alerts is for after-the-fact investigation. The operator who is paged at 03:00 needs alerts that fire before the problem becomes user-visible. The Prometheus alerting rule format:
groups:
- name: opnsense
rules:
- alert: OPNsenseStateTableNearFull
expr: opnsense_pf_states_total > 8000
for: 5m
labels:
severity: warning
annotations:
summary: 'OPNsense state table near full on {{ $labels.device }}'
description: 'Current entries: {{ $value }} of 10000 limit.'
- alert: OPNsenseStateTableExhausted
expr: opnsense_pf_state_limit_drops_total > 0
for: 1m
labels:
severity: critical
annotations:
summary: 'OPNsense dropping flows — state table exhausted'
description: 'Drops detected on {{ $labels.device }}.'
- alert: OPNsenseHighCPU
expr: 100 - (avg by (device) (rate(opnsense_cpu_idle_total[5m])) * 100) > 85
for: 10m
labels:
severity: warning
annotations:
summary: 'OPNsense CPU above 85% on {{ $labels.device }}'
- alert: OPNsenseInterfaceDrops
expr: rate(opnsense_interface_drops_in_total[5m]) > 100
for: 5m
labels:
severity: warning
annotations:
summary: 'Interface drops above threshold on {{ $labels.device }} {{ $labels.interface }}'
The for: clause is critical — it prevents the alert from firing on a transient spike. A state table at 95% for 30 seconds during a connection burst is not the same as a state table at 95% for 5 minutes sustained.
Dashboards that survive an incident
The dashboard the operator opens at 03:00 is the one that has been maintained in calm conditions. A dashboard with stale thresholds, broken panel queries, or undefined variables is a dashboard the operator will mistrust and abandon. The disciplines:
- Test panels with the Prometheus data source, not the example data. The community dashboard may have been built against a different exporter version; queries may break.
- Set thresholds from the operator’s actual SLOs. A state-table alert at 90% of the default 10000 limit is too high for a busy firewall; tune to the deployment.
- Document the panels. Each panel should have a description that says what the metric means and why the threshold is what it is. The description is read by the next operator on shift.
- Use variables. A dashboard with a
$devicevariable that filters by firewall name lets one dashboard serve multiple firewalls. - Time ranges matter. Default to “last 6 hours” for triage dashboards and “last 30 days” for trend dashboards.
Summary
- Grafana is the visual layer on Prometheus — the data source is the plumbing, the dashboards are the triage tool.
- Community OPNsense dashboards are useful starting points but must be adapted to the deployment.
- The panels that matter, in triage order: state table, CPU, memory, interface counters, log volume, CARP.
- Alert rules should fire on metrics that require operator action, not on every metric the exporter exposes.
- Dashboards must be maintained in calm conditions to be trusted during an incident.
Knowledge check · 4 questions
Q1. Why is the order of panels on a triage dashboard important?
Q2. A Prometheus alert rule with for: 30s will fire as soon as the expression is true for 30 seconds.
Q3. Which of the following should be alerted on, rather than just graphed? Select all that apply.
Q4. The operator has imported a community OPNsense dashboard and several panels are broken. What is the most likely cause?
Passing score: 75%. Answers are checked in this browser.