pfSense is a FreeBSD-based firewall and router platform that gives you enterprise-grade network control on commodity hardware. This writeup covers the full setup from installation through to a working multi-VLAN configuration with DNS over TLS, plus the reasoning behind a few decisions that aren’t obvious until you’ve hit the alternative.
Why pfSense
I evaluated OPNsense, pfSense, and a plain Linux iptables/nftables setup. pfSense won for three reasons: the volume of documentation and community troubleshooting available (genuinely useful when something breaks at 11pm), the maturity of the package ecosystem (pfBlockerNG, Suricata, and others are all first-class rather than bolted on), and the WebGUI, which makes complex firewall rule management approachable without sacrificing the depth a raw iptables setup would give you.
OPNsense was a close second โ technically more modern in places โ but pfSense’s larger community and my own prior familiarity with its rule-processing model tipped it.
Running pfSense in Proxmox
pfSense runs as a full VM rather than an LXC โ unlike Linux-based services, it needs its own kernel for the FreeBSD network stack, so containerisation isn’t an option here.
CPU: 2 cores (KVM64)
RAM: 2048MB
Disk: 16GB (virtio-scsi)
NIC 1: vtnet0 โ WAN (bridged to physical NIC, no VLAN tag)
NIC 2: vtnet1 โ LAN bridge (trunk port, carries all internal VLAN tags)
One detail that cost some debugging time initially: pfSense’s FreeBSD network stack doesn’t always play nicely with VirtIO NIC drivers depending on the pfSense/FreeBSD version โ if you see intermittent packet loss or the interface flapping under load, switching the NIC model to e1000 (Intel emulated) in the Proxmox VM hardware settings is a known workaround, at a small performance cost that’s irrelevant at home-network throughput levels.
VLAN configuration
Configured under Interfaces โ Assignments โ VLANs โ Add:
VLAN 10 โ DMZ Parent: vtnet1 Tag: 10
VLAN 20 โ IoT Parent: vtnet1 Tag: 20
VLAN 30 โ Trusted Parent: vtnet1 Tag: 30
Each VLAN then needs assigning as its own interface (Interfaces โ Assignments) and enabling with its own subnet and DHCP scope โ pfSense treats each VLAN as a fully independent interface for firewall rule purposes, which is what makes the per-VLAN rule sets below possible.
Firewall rules
Rules are configured per-interface under Firewall โ Rules, with each VLAN’s tab holding only the rules that apply to traffic originating from that VLAN.
DMZ rules:
Allow DMZ โ WAN (outbound updates)
Block DMZ โ Trusted LAN
Block DMZ โ IoT
Block DMZ โ RFC1918 (catch-all private ranges โ belt and braces beyond the two explicit blocks above)
IoT rules:
Allow IoT โ WAN
Block IoT โ Trusted LAN
Block IoT โ DMZ
The block rules must come before any allow rules in each interface’s list โ pfSense processes rules top to bottom per-interface and stops at the first match, same as most stateful firewalls. A broad “allow all outbound” rule sitting above a specific block rule silently defeats it, which is an easy trap to fall into when adding a rule for a specific troubleshooting session and forgetting to reorder afterward.
The Block DMZ โ RFC1918 rule is deliberately redundant with the two explicit VLAN blocks above it โ RFC1918 covers all private address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), so it’s a catch-all that would also block a future VLAN I forget to add an explicit rule for. Defence in depth over relying on remembering to update every rule set every time the network topology changes.
DNS over TLS
Under Services โ DNS Resolver โ General Settings, DNS over TLS is enabled with upstream servers configured over port 853, encrypting all DNS queries leaving the network rather than sending them in plaintext to the ISP’s resolver (or anyone positioned to see that traffic in between).
DNS Query Forwarding: Enabled
Forwarding servers:
1.1.1.1 port 853 (TLS)
1.0.0.1 port 853 (TLS)
DNSSEC: Enabled
Worth noting: enabling DNS over TLS here encrypts the resolver’s upstream queries, not necessarily every client device’s own DNS behaviour โ a client hardcoded to use 8.8.8.8 directly bypasses this entirely. Combined with a firewall rule blocking outbound port 53 to anything except the pfSense resolver itself, this forces all DNS through the encrypted path regardless of individual device configuration.
Result
Running on 2 cores and 2GB RAM, pfSense handles gigabit throughput comfortably, with CPU usage sitting around 2-3% under normal load โ significantly over-specced for a home lab, which leaves plenty of headroom for adding Suricata IDS/IPS or pfBlockerNG later without a hardware upgrade. The VLAN separation and explicit block-before-allow rule ordering means a compromised IoT device or DMZ server has no direct path to the trusted network, regardless of what it tries.