A plugin that emails a summary of your network on a schedule: how much data went through the internet connection, whether the gateways dropped, what the DNS blocklists caught, what the IDS saw, which devices were busiest, and how the firewall itself is doing.
I wanted something I could read over coffee instead of opening five pages of the GUI every morning, so the report is a single HTML mail with the things that need attention at the top.
Tested on OPNsense 26.7.
A daily report (sample data).
- Needs attention: gateway down or losing packets, outage alarms, new devices, high severity IDS alerts, temperature or disk above a threshold, SMART problems, an old configuration backup, the monthly data cap getting close.
- Internet usage: download and upload for the period, month to date, traffic through VPN tunnels, compared with the previous period.
- Connection quality: state, latency and loss of each monitored gateway, outage alarms in the period.
- DNS filtering: queries, blocked queries, most blocked domains, busiest clients.
- Security: Suricata alerts by signature and source, CrowdSec alerts and active bans.
- Devices: devices online, devices seen for the first time (with the manufacturer), and the heaviest users.
- Firewall health: version, uptime, load, temperature, memory, disk, SMART, last backup, VPN tunnel state.
Each schedule picks which of these sections it includes.
- Any number of schedules: daily, on selected weekdays, or on a day of the month (including the last day). Each has its own time, period, language, sections and recipients.
- The period defaults to "since the previous scheduled report", so nothing is counted twice. Fixed periods (24 hours, 7 days, 30 days, previous calendar month, month to date) are also available.
- Reports in all 21 languages of the OPNsense GUI, or in whatever language the GUI is set to. Arabic and Persian reports are laid out right to left.
- Optional "only when something needs attention" mode, which skips quiet days.
- A short alert within five minutes when a device joins the LAN for the first time.
- If the firewall is off at the scheduled time, the report goes out when it comes back (up to 12 hours late).
- Preview and "send now" buttons, a history of everything sent, and copies of sent reports kept on the firewall for a configurable number of days.
- Mail goes through the SMTP server already configured for Monit, or a separate one.
The GUI below is shown in Arabic, with the right-to-left layout from opnsense-rtl; in English it is laid out the usual way.
| Schedules | Editing a schedule |
|---|---|
![]() |
![]() |
The same report in Arabic: docs/report-ar_SA.png.
The plugin does not collect anything itself; it reads what OPNsense already records. Sections whose source is missing simply show "no data".
| Section | Source | Needed |
|---|---|---|
| Internet usage | vnstat | os-vnstat plugin |
| Connection quality | dpinger gateway monitoring, system log | gateway monitoring enabled |
| DNS filtering | Unbound query database | Unbound with reporting enabled |
| Security | Suricata eve.json, cscli |
Intrusion Detection, os-crowdsec (optional) |
| Devices | ARP table, host discovery, NetFlow | Insight (NetFlow) for per-device usage |
| Firewall health | sysctl, smartctl, system log |
os-smart for SMART (optional) |
The plugin is not in the OPNsense plugin repository yet, so there is no
pkg install os-netreport. There is a package, and you build it — on the firewall,
from this repository, in one command. Building needs no root and installs nothing.
fetch -o /tmp/netreport.tar.gz https://github.com/AbdelmonemAwad/os-netreport/archive/refs/heads/main.tar.gz
tar -xzf /tmp/netreport.tar.gz -C /tmp
cd /tmp/os-netreport-main
sh tools/make-package.sh -o /tmp -c netIt tells you where it put the package, its digest, and which commit it came from:
/tmp/os-netreport-1.0.pkg
SHA256 fa87eefb3acd9c1d2094e00e7816a84ae2f52804fb5c195cecea9f64b5f1c417
commit 3ba5f0ad1
built on OPNsense 26.7 amd64
The digest is of the file you just built. Two builds of the same commit do not produce the same digest unless the commit date is passed in, because otherwise every file in the archive carries its own mtime:
SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) sh tools/make-package.sh -o /tmpRead it before you install it. Nothing about a package file makes it trustworthy, and both of these questions are answered out of the file itself, without touching the machine:
pkg info -F /tmp/os-netreport-1.0.pkg # version, licence, dependencies, description
pkg info -lF /tmp/os-netreport-1.0.pkg # every file it will write, and it writes no othersThen, as root:
pkg add /tmp/os-netreport-1.0.pkgThen open Reporting → Network Report, check the mail settings on the Settings tab (the Save and send test message button is the quickest way), and add a schedule.
A newer version goes on over an installed one with pkg add -f. Settings in config.xml and
state under /var/db survive, because pkg replaces only the files the package owns.
Installing by hand instead, with no package
install/install.sh does the same work and is what existed before there was a package:
fetch -o /tmp/netreport.tar.gz https://github.com/AbdelmonemAwad/os-netreport/archive/refs/heads/main.tar.gz
tar -xzf /tmp/netreport.tar.gz -C /root
sh /root/os-netreport-main/install/install.shThe difference is only in the bookkeeping: pkg does not know the files are there, so
opnsense-version -c os-netreport answers not installed on a machine where the plugin is
running, and removal is install/uninstall.sh rather than pkg delete.
pkg delete os-netreportSettings stay in config.xml and the history in /var/db/netreport, so a reinstall
picks up where it left off.
It is not upgraded by System → Firmware → Updates, because that only offers what the
OPNsense repository carries, and this is not in it. A firmware update does not remove it —
the files are pkg's, and OPNsense does not delete packages it did not install — but it will
not be updated either. Build a new package from a newer tag and pkg add -f it.
src/opnsense/scripts/netreport/netreport.pybuilds and sends the reports. Cron runs it every minute (/usr/local/etc/cron.d/netreport); it sends whatever is due and scans the LAN for new devices every five minutes.- The GUI (model, controllers, view) follows the usual OPNsense MVC layout. Settings are stored
under
OPNsense/NetReportinconfig.xml. - Report texts live in
i18n/<locale>.json. GUI strings for languages other than English are ini18n/ui/<locale>.jsonand are merged into the system catalogs bymerge_ui_translations.py, which also runs when pkg writes a catalog (/usr/local/share/pkg/triggers/os-netreport.ucl) and at boot, because core updates replace those catalogs and an update that only installs packages does not reboot. Once the plugin is part of the official repository this step goes away, since its strings would then be translated through the normal OPNsense process. - State, history and archived reports are kept in
/var/db/netreport.
English and Arabic are complete and in daily use. The other languages are first versions and
would benefit from a native speaker's review; corrections are welcome as pull requests.
install/check_i18n.py checks that every language file has the same keys and placeholders as
the English one.
- Per-device usage only covers traffic that passes through the firewall; traffic between two devices on the same switch is not seen.
- The "new device" list relies on the ARP table, so devices that are only on IPv6 are not detected.
- Some languages change word forms with the number (Polish, Russian, Ukrainian and others). The wording was chosen to read well for most numbers, but not every count is grammatically perfect.
xgs-image-builder builds an OPNsense image
for Sophos XGS appliances with this repository in it, installed at the image's first boot. It holds
no copy of this repository: every build takes main as it is that day, so a change merged here is
in the next image without anything being done there. What the image calls here - install/install.sh - is checked
from that side every day, so moving or renaming one of them is caught there; keep them where they are.
An installed copy is updated to this repository's releases by hand: from XGS: Updates, its Plugins
tab (os-xgs-npu), or with /usr/local/opnsense/scripts/netreport/self-update.py. Once a day, at 03:17, from - nothing
is installed on its own since 2026-10-20, at the owner's word. It asks GitHub for the latest release and, when it is ahead of what
is installed, downloads it and runs the /usr/local/etc/cron.d/netreport-updateinstall/install.sh it carries - the same script a hand install
runs. A change merged on main reaches installed firewalls when it is released, not before; with
no release published, nothing changes.
- Only forward. What is installed is recorded in
/usr/local/share/os-netreport/installed.jsonby the installer - the commit, from a git checkout or from the.source-commitstamp that an xgs-image-builder image or an update leaves in the tree. A firewall running something newer than the release is not taken back; an install from before the stamp existed takes the latest release once. - What happened is in the system log as
self-update, in/usr/local/share/os-netreport/update.json, and the installer's output inupdate.logbeside it. /usr/local/opnsense/scripts/netreport/self-update.py checksays what it would do and changes nothing.
Tested in a QEMU virtual machine against a stand-in for GitHub (no release had been published): a release ahead is installed, one that is not is left, no release and an unreachable GitHub change nothing, and the uninstaller removes the updater, its cron entry and the stamp.
BSD 2-Clause, the same as OPNsense. See LICENSE.
The photographs and screenshots under docs/ are © 2026 Abdelmonem Awad, under CC BY 4.0: use them freely, naming the author and linking here. Each carries that notice in a strip added below it.


