pfSense and OPNsense are the default homelab firewall recommendation. The recommendation is often correct — and sometimes not. The decision depends on understanding the architectural trade-off between the platform model and the integrated appliance model. Here is the technical picture.
The platform model's dependency graph
Reaching full UTM functionality on pfSense/OPNsense requires assembling a plugin stack. Each plugin introduces a dependency node:
pfSense/OPNsense (FreeBSD base)
├── Squid package (web proxy)
│ ├── c-icap (ICAP connector)
│ │ └── ClamAV (antivirus engine)
│ └── ssl-bump (TLS inspection)
│ └── CA certificate (PKI management)
├── SquidGuard or pfBlockerNG (URL filtering)
│ └── Category blocklists (external URLs)
├── Suricata or Snort (IDS/IPS)
│ └── Rule sources (ET, Snort VRT)
└── ModSecurity (WAF — limited integration path)
Each arrow represents a compatibility surface. When the FreeBSD base updates (pfSense 2.x → 2.x+1), each package must also update. If a package maintainer is behind, you have three options:
- Skip the FreeBSD update (accumulate security debt)
- Update and break the package (lose functionality)
- Wait for the package to catch up (delays on your timeline)
The c-icap → ClamAV boundary is particularly fragile. ClamAV major versions (0.103 → 0.104 → 1.x) changed API interfaces that broke c-icap compatibility. The result: after a ClamAV update, the antivirus silently stops scanning traffic — Squid receives no ICAP error, just an empty response, and continues delivering unscanned content.
Detection requires actively monitoring ICAP responses:
# Verify ICAP connectivity and ClamAV health
echo -e "OPTIONS icap://127.0.0.1:1344/squid_clamav ICAP/1.0\r\nHost: 127.0.0.1\r\n\r\n" | nc 127.0.0.1 1344
A healthy response: ICAP/1.0 200 OK. Silent failure: connection refused or no response — but Squid is not told, and content keeps flowing unscanned.
Packet filtering: FreeBSD pf vs Linux nftables
pfSense/OPNsense uses OpenBSD's pf. Last-match-wins semantics, stateful firewall, excellent NAT support:
# pf rule syntax
pass out on em0 proto { tcp, udp } from any to any
block in on em0 proto tcp from <blocklist>
Linux (CacheGuard, custom LFS) uses nftables (or iptables). First-match-wins with explicit jumps, better performance at scale via sets and maps:
# nftables equivalent
nft add rule inet filter input tcp dport { 80, 443 } accept
nft add rule inet filter input ip saddr @blocklist drop
At homelab traffic volumes (< 1 Gbps), neither has a measurable performance advantage. The syntactic differences matter more in practice than the performance characteristics.
The integrated appliance model
An integrated appliance OS ships all UTM components from a single versioned release. The dependency graph collapses:
CacheGuard-OS (custom LFS-based Linux)
└── All components version-locked at release time:
Squid + ClamAV + c-icap + ModSecurity + StrongSwan + iproute2
↑
Tested together before release. No inter-component compatibility surface.
When CacheGuard updates, the Squid → c-icap → ClamAV chain has been verified by the release process. There is no scenario where ClamAV updates independently and silently breaks ICAP.
Trade-off: you cannot substitute components. If you want a different proxy engine, a custom Suricata ruleset at the config level, or per-packet access to the BSD network stack, the integrated model is a constraint, not a feature.
When to choose which
Choose pfSense/OPNsense if:
- Learning network security configuration is the explicit goal (plugin assembly = education)
- You need advanced routing (BGP, OSPF, MPLS simulation)
- You require IPS with custom Suricata/Snort rules
- Your lab simulates enterprise BSD network environments
Choose integrated appliance (CacheGuard) if:
- The security layer is infrastructure for other lab work, not the lab work itself
- You want a working Squid+ClamAV+WAF stack without integration debugging
- You prefer a single update operation over coordinated multi-package updates
- You are running a self-hosted homelab with production-like security requirements
Both run on identical x86 hardware. Both are free. Both support multiple network interfaces and VLAN trunking.
→ https://www.cacheguard.com/homelab-firewall/
Originally published on the CacheGuard Blog. CacheGuard is free and open source — GitHub.

Top comments (0)