Skip to content

OPNsense in Proxmox

Published: at 12:00 AM

OPNsense in Proxmox: chasing a phantom packet loss bug (and the VLAN fix that finally worked)

I run OPNsense as a VM inside Proxmox, connected to a VLAN-tagged network via a trunk (Trk1) to an HP 2530 switch. Recently I started seeing something odd: 6-10% packet loss on traffic forwarded through OPT1 to LAN/WAN, but 0% loss when pinging directly from the Proxmox host to OPT1’s address. Here’s how I tracked it down — including the dead ends.

First suspect: VirtIO hardware offload

The classic villain in Proxmox + OPNsense setups is hardware checksum/TSO/LRO offload on VirtIO NICs. It’s notoriously buggy specifically on forwarded traffic, which matched my symptom perfectly — a direct ping to an interface doesn’t trigger the same code path as traffic actually routed between interfaces.

I disabled it in System → Advanced → Networking:

I verified with ifconfig that the flags were actually gone from the interface:

options=880008<VLAN_MTU,LINKSTATE,HWSTATES>

No RXCSUM, TXCSUM, TSO, or LRO left. Rebooted the VM to be sure the settings fully applied.

Result: no change. Whatever this was, it wasn’t the guest-side offload.

Digging deeper: size-dependent loss

I tested with a don’t-fragment, near-MTU-size ping:

ping -D -s 1472 8.8.8.8

and got roughly 50% loss on large packets going out through OPT1 to the internet, while small pings were mostly fine. That pointed at something size-sensitive happening specifically on forwarded traffic — consistent with an offload-style bug, but now ruled out at the guest level. That meant the problem was more likely sitting below OPNsense, in the Proxmox bridge itself.

The VLAN-aware bridge theory

Proxmox’s Linux bridge can operate in two modes:

VLAN-aware bridges combined with VirtIO multiqueue have a history of odd bugs around tagged-frame handling, especially at larger packet sizes.

My fix attempt: stop tagging at the Proxmox/vNIC level entirely, give OPNsense a raw trunk, and let OPNsense create its own VLAN sub-interface — the “textbook correct” way to do it, in theory.

When the “correct” fix broke everything

After the migration, the new VLAN interface was completely dead:

Tracing the path hop by hop with tcpdump across vtnet0 → vmbr0 → bond0 → Trk1 eventually showed the ARP requests were leaving OPNsense and reaching the Proxmox bridge — and even the physical bond showed full ARP request/reply pairs plus live DNS and ICMP traffic from other hosts on the VLAN. The VLAN was healthy at the switch and bridge level. But reachability from OPNsense to devices on that VLAN still failed.

The actual fix: dedicated Linux VLAN + bridge per VLAN

Instead of a shared VLAN-aware bridge, or pushing VLAN handling into OPNsense’s own OS, I created a fully separate path for this VLAN in Proxmox:

This way, VLAN tagging and untagging happens exactly once, cleanly, in the kernel’s VLAN driver — before traffic ever reaches a shared bridge or the VM. OPNsense’s vNIC just sees plain untagged Ethernet on its own dedicated bridge, with zero ambiguity about tagging or bridge-level VLAN filtering.

This fixed it immediately.

Takeaways