OPNsenseXXXIII · Logging and Remote LoggingLogging architecture
Logging architecture on OPNsense — where the logs come from and where they go
What you'll learn
- Identify the components that produce logs on OPNsense
- Trace a log message from producer to syslog-ng to disk to GUI
- Recognise the default log locations and rotation behaviour
- Locate the integration points where logs leave the firewall
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-18
Logging on OPNsense looks simple from the GUI — there are Log Files menus, there are tabs, there is a search box. Underneath there are a dozen components producing messages, syslog-ng routing them by the program that emitted them, one plain-text file per application per day on disk, and integration points where messages leave the firewall for a remote destination. The operator who knows this architecture finds logs faster, configures remote forwarding correctly, and stops guessing about where a given message landed.
One directory per application, one file per day
OPNsense runs syslog-ng as its log daemon. Every local destination follows the same naming convention:
/var/log/<application>/<application>_YYYYMMDD.log
Alongside the daily files, each directory carries a latest.log symlink pointing at the newest one. These are ordinary text files with ordinary permissions — cat, tail -F, grep, awk, and less all work directly on them.
| Component | Program name syslog-ng matches | Log directory |
|---|---|---|
| pf (packet filter) | filterlog | /var/log/filter/ |
| Firewall subsystem messages | firewall | /var/log/firewall/ |
| System (kernel, daemons, catch-all) | everything unmatched at notice and above | /var/log/system/ |
| Authentication and audit | audit, plus the auth facility | /var/log/audit/ |
| Backend (configd, API) | configd.py, api | /var/log/configd/ |
| Unbound (DNS resolver) | unbound | /var/log/resolver/ |
| Dnsmasq (DNS/DHCP) | dnsmasq | /var/log/dnsmasq/ |
| Kea (DHCP) | kea-dhcp4, kea-dhcp6, kea-ctrl-agent, kea-dhcp-ddns | /var/log/kea/ |
| IPsec (strongSwan) | charon | /var/log/ipsec/ |
| OpenVPN | openvpn | /var/log/openvpn/ |
| WireGuard | wireguard, plus kernel messages naming a wg interface | /var/log/wireguard/ |
| Suricata (IDS/IPS) | suricata | /var/log/suricata/ |
| Gateway monitoring | dpinger | /var/log/gateways/ |
| Dynamic routing | bgpd, ospfd, zebra, olsrd, routed | /var/log/routing/ |
| NTP | ntpd, ntp, ntpdate | /var/log/ntpd/ |
| Web GUI server | lighttpd | /var/log/lighttpd/ |
| Service watchdog | monit | /var/log/monit/ |
| Package management | pkg, pkg-static | /var/log/pkg/ |
| Captive portal | captiveportal | /var/log/portalauth/ |
Plugins add their own destinations the same way, so an installed plugin generally brings a new directory under /var/log/ rather than a new file in an existing one.
The syslog-ng pipeline
Everything on the box logs to the syslog socket. syslog-ng collects all of it into a single source and then splits it with filters. The filters key almost entirely on the program name of the sender, not on the syslog facility.
[ kernel, daemons, plugins, GUI ]
|
v
syslog-ng (source s_all)
|
+-------------+---------------------------+
| |
v v
filter by program name remote destination(s)
| UDP / TCP / TLS
v
/var/log/<app>/<app>_YYYYMMDD.log
|
v
GUI Log Files pages (via configd)
A local filter is one small definition per application. The filter for the firewall log is the whole of it:
filter f_local_filter {
program("filterlog");
};
Anything at notice severity or above that no application filter claims falls through to /var/log/system/. That fall-through is why the system log is the right first stop when you do not know which component produced a message.
Rotation and retention
Rotation is not size-triggered by default and does not produce compressed generations. syslog-ng simply starts a new file when the date changes, and a housekeeping job trims the old ones. That job — configctl syslog archive, listed in the cron picker as “Rotate log files” — runs automatically every hour and does three things per log directory:
- If Maximum file size is set and the current file is over it, renames the file with a sequence suffix and restarts
syslog-ngso a fresh file starts. Left empty, which is the default, this step never fires and the daily file grows without limit. - Deletes the oldest files beyond Maximum preserved files.
- Repoints
latest.logat the newest file.
Both settings live under System → Settings → Logging, on the “Local” tab. Maximum preserved files defaults to 31. Because the default is one file per day, that is roughly a month of history per application, held on disk, readable, and greppable.
$ ls /var/log/filter/; echo '---'; du -sh /var/log/filter /var/log/system /var/log/auditfilter_20260715.log filter_20260726.log filter_20260806.log
filter_20260716.log filter_20260727.log filter_20260807.log
filter_20260717.log filter_20260728.log filter_20260808.log
filter_20260718.log filter_20260729.log filter_20260809.log
filter_20260719.log filter_20260730.log filter_20260810.log
filter_20260720.log filter_20260731.log filter_20260811.log
filter_20260721.log filter_20260801.log filter_20260812.log
filter_20260722.log filter_20260802.log filter_20260813.log
filter_20260723.log filter_20260803.log filter_20260814.log
filter_20260724.log filter_20260804.log latest.log
filter_20260725.log filter_20260805.log
---
612M /var/log/filter
41M /var/log/system
7.2M /var/log/audit
Illustrative output
GUI exposure
The GUI does not keep a second copy of anything. Each Log Files page calls into configd, which reads the same files from /var/log/ and returns parsed records.
| Page | Reads |
|---|---|
| Firewall → Log Files → Live View | /var/log/filter/, parsed into named fields, with a live tail |
| Firewall → Log Files → Plain View | /var/log/filter/, as raw lines |
| Firewall → Log Files → Overview | /var/log/filter/, aggregated into counts |
| Firewall → Log Files → General | /var/log/firewall/ |
| System → Log Files → General | /var/log/system/ |
| System → Log Files → Backend | /var/log/configd/ |
| System → Log Files → Audit | /var/log/audit/ |
| System → Log Files → Web GUI | /var/log/lighttpd/ |
| System → Log Files → Boot | the boot log |
Services with their own logs expose them on their own pages rather than under System — the IPsec log sits under VPN, the resolver log under Services, and so on.
Because the pages and the shell read the same files, the GUI is a genuine search tool over the whole retained window, not a live-only view. What it cannot show you is anything already deleted by the retention limit, which is a different constraint and a much more predictable one.
$ configctl syslog status; echo '---'; configctl filter read log 1Checking syslog-ng status. Running
---
[{"rulenr":"7","subrulenr":"","anchorname":"","rid":"02f4bab031b57d1e30553ce08e0ec131","interface":"igb0","reason":"match","action":"block","dir":"in","ipversion":"4","protoname":"tcp","src":"198.51.100.23","dst":"192.0.2.10","srcport":"40254","dstport":"3389","label":"Default deny / state violation rule","__timestamp__":"2026-08-14T16:25:31","__host__":"opnsense.example.com"}]
Illustrative output
Integration points
The points at which logs leave the firewall:
-
Remote syslog forwarding — the primary one. System → Settings → Logging, “Remote” tab. UDP, TCP, or TLS over IPv4 or IPv6, with optional filters on application, severity, and facility. Forwarding runs in parallel with local storage; it does not replace it.
-
The API — the same configd calls the GUI uses are reachable over the REST API, returning JSON. Useful for scripted collection and dashboards, not for bulk shipping.
-
Plugin-specific outputs — some components write their own files outside the syslog-ng scheme and rotate them with
newsyslog. Suricata’s EVE JSON at/var/log/suricata/eve.jsonis the one most estates ship directly to a SIEM; the Squid access log behaves similarly. -
The shell — direct read access over SSH.
grep,awk, andtail -Fover/var/log/<app>/need no tooling beyond the base system.
What lives where
Quick reference:
| Need | Where to look |
|---|---|
| “Why is traffic being blocked?” | /var/log/filter/, or Firewall → Log Files → Live View |
| “Did a rule change get applied?” | /var/log/firewall/, /var/log/configd/ |
| “Did the system reboot? Why?” | /var/log/system/, and dmesg |
| “Who logged in? Failed logins?” | /var/log/audit/ |
| “Did the VPN tunnel come up?” | /var/log/ipsec/, /var/log/openvpn/, /var/log/wireguard/ |
| “What did a client resolve in DNS?” | /var/log/resolver/ or /var/log/dnsmasq/ |
| “Which client got which lease?” | /var/log/kea/ or /var/log/dnsmasq/ |
| “Is a gateway flapping?” | /var/log/gateways/ |
| “Did the IDS alert on this?” | /var/log/suricata/, and /var/log/suricata/eve.json |
Summary
- OPNsense logs through
syslog-nginto plain text, one directory per application, one file per day, plus alatest.logsymlink. - Routing is by program name, not by syslog facility, and unclaimed messages fall through to
/var/log/system/. clogis the pfSense circular-log tool. It is not how OPNsense stores or reads logs.- Retention is Maximum preserved files under System → Settings → Logging, defaulting to 31 files, which is about a month. It bounds the file count, not the disk footprint.
- The GUI, the API, and the shell all read the same files through configd.
- Remote syslog forwarding is the integration point for centralised retention.
Knowledge check · 3 questions
Q1. An operator is investigating why traffic is being blocked and wants the packet records pf wrote. Which path holds them?
Q2. With default settings, OPNsense keeps roughly a month of firewall log history on disk, and the size of that history depends on how much traffic is logged.
Q3. Which of the following are valid ways to get at OPNsense log data? Select all that apply.
Passing score: 75%. Answers are checked in this browser.