VLAN Tagging for Proxmox VE: Bridges, Containers & Firewall Rules
Learn how VLAN tagging works in Proxmox — from bridge pvid and tagged-vlans to LXC overrides and nftables rules. Stop cross-segment bleed without extra hardware.
On this page
The most common confusion with VLANs on Proxmox isn't the configuration itself — it's understanding which packets get tagged, when they do so, and how that interacts with your switch ports. This guide walks through practical patterns for tagging traffic across bridges, KVM guests, LXC containers, and firewall rules, showing you exactly what to configure in /etc/network/interfaces and why each option matters on real hardware.
Key Takeaways
- Bridge config —
pvidassigns a default VLAN ID whiletag:controls which tagged egress packets the bridge sends out; Proxmox treats both together as access-port behavior - LXC vs KVM tagging — LXC containers inherit their parent bridge's pvid by default, but you can override it with an explicit tag in
/etc/pve/lxc/<id>.confwithout touching host config - Firewall layering — Proxmox firewall rules evaluate per-VLAN using nftables chains; setting
tag:<vlan>on a rule scopes that match to the VLAN, avoiding cross-segment bleed - Tagged vs untagged ports — Access ports (untagged) are simpler for end devices but force all traffic onto one pvid; trunk/tagged ports carry multiple VLANs and require host-side tagging decisions
How Do Bridges Tag on Proxmox?
The Linux bridge behind every Proxmox VE node handles both tagged and untagged frames. What most people miss is that pvid doesn't tag incoming packets — it assigns a default ID when an untagged frame arrives, while the tag: property controls which VLAN IDs get added to egress traffic on top of whatever was already there.
Consider this bridge definition in /etc/network/interfaces:
auto vmbr0
iface vmbr0 inet static
address 192.168.1.5/24
gateway 192.168.1.1
bridge-ports eth0
bridge-pvid 100
bridge-tagged-vlans '30,70'
Here's what actually happens: an untagged packet arriving on eth0 gets assigned pvid 100 inside the bridge. A tagged packet with VLAN ID 30 passes through unchanged and is also forwarded out as tag 30 because of bridge-tagged-vlans. If you set both, traffic for VLANs in bridge-tagged-vlans leaves with their tags intact; everything else gets stripped to pvid (or stays untagged if no pvid exists).
This distinction matters when your upstream switch expects tagged frames. A common mistake is setting only pvid 100 without any bridge-tagged-vlans, which causes all traffic on that bridge to emerge as a single VLAN — defeating the purpose of having multiple subnets behind one physical port.
The same behavior applies whether you're running KVM guests or LXC containers on vmbr0, but there's an important nuance for container-level overrides that I'll cover later in this article.
Tagged vs Untagged Ports: When Each Makes Sense
Access ports (untagged) are the simpler pattern and work well when your Proxmox host connects to a single switch port carrying all traffic — which is typical of most homelab setups where you're using an unmanaged or basic managed switch. Your VMs on that bridge see everything as if they were directly wired together, with VLAN IDs applied transparently at the bridge level.
Trunk ports (tagged) come into play when your Proxmox host needs to carry multiple distinct networks over a single uplink — for instance, separating management traffic from storage and guest workloads on one cable rather than running separate NICs or bonding them together later. This is where VLAN tagging patterns become genuinely useful beyond "having more subnets."
For homelab setups with fewer than 10-15 VLANs, access ports are usually sufficient and easier to debug when something breaks. The tradeoff: you lose the ability to isolate traffic at Layer 2 without relying on firewall rules or routing between bridges (which Proxmox handles via ip_forward and iptables/nftables).
When your network grows beyond that — say, adding a dedicated VLAN for backup storage like iSCSI targets from TrueNAS SCALE, another for monitoring agents, and yet more for isolated test environments — trunk ports with explicit tagging give you finer control. You can selectively tag traffic to specific bridges without creating additional physical interfaces or virtual sub-interfaces on the host itself.
How Do I Configure VLANs Across Bridges?
The most common production pattern is a single bridge carrying everything, but it's worth knowing how Proxmox handles multiple tagged networks before you start adding complexity. Here are two approaches that actually work in practice:
Single bridge with mixed tags (most common):
auto vmbr0
iface vmbr0 inet static
address 192.168.1.5/24
gateway 192.168.1.1
bridge-ports eth0
bridge-pvid 100
bridge-tagged-vlans '30,70'
Multiple sub-interfaces on one physical port:
auto vmbr0
iface vmbr0 inet static
address 192.168.1.5/24
gateway 192.168.1.1
bridge-ports eth0
auto vmbr1
iface vmbr1 inet manual
bridge-ports none
bridge-tagged-vlans '30'
auto vmbr70
iface vmbr70 inet static
address 192.168.70.5/24
gateway 192.168.70.1
bridge-ports eth0
bridge-tagged-vlans '70'
The first approach is simpler and avoids the subtle gotcha I mentioned above — where untagged packets on a single pvid-only bridge don't get their tags preserved when they leave toward other bridges or external networks. The second gives you isolation at the bridge level, which means firewall rules scoped to vmbr70 won't accidentally affect traffic on vmbr1.
VLAN Tagging in LXC Containers and KVM Guests
LXC containers inherit network configuration from their parent VM's bridge by default — this is where many people trip up when they expect a container on vmbr0 (pvid 100) to appear as untagged traffic for all subnets. The fix: set an explicit tag in the LXC config file at /etc/pve/lxc/<id>.conf:
net0: name=eth0,bridge=vmbr0,gw=auto,hwaddr=<MAC>,ip=dhcp,type=veth,vlan.id=30
The vlan.id property overrides the inherited pvid and forces that container's traffic to egress with VLAN 30 tagged — regardless of what vmbr0's default is. This matters when you're running services like Docker inside an LXC (see Docker Inside Proxmox LXC: A Practical Guide for the storage driver implications) and need those containers to appear on a specific VLAN segment without touching host-level config.
For KVM guests, tagging works differently because each virtual NIC connects directly to its parent bridge. The guest OS itself handles 802.1Q tagging if you configure sub-interfaces inside it (e.g., eth0.30 in Linux), or you can use the Proxmox GUI's "Tag" field on a VM's network device — which tells the host to tag all traffic from that NIC regardless of what the guest sends:
net0=virtio,bridge=vmbr0,vlan-tagged=70
KVM guests give you more flexibility for complex tagging scenarios (like running multiple VLANs on a single virtual NIC) but require either host-side or guest-side configuration depending on your topology. LXC containers are simpler when the parent bridge handles most of the heavy lifting, which is why many homelabbers prefer them for services that don't need their own IP address space.
How Do I Layer Firewall Rules Per-VLAN?
Proxmox's firewall uses nftables under the hood and evaluates rules per-VM or per-cluster with VLAN scope built in — no extra configuration required once your bridges are tagged correctly. The key property is tag:<vlan> on a rule, which restricts that match to traffic carrying the specified VLAN ID:
table inet fw4 {
chain input {
type filter hook input priority 0; policy drop;
iifname "vmbr30" tcp dport ssh accept tagged vmbr30
ip saddr 192.168.70.0/24 tagged vmbr70 accept
}
chain forward {
type filter hook forward priority -5; policy drop;
vlan_id 30 tcp dport 80,443 accept
vlan_id 70 ip protocol icmp accept
}
}
A common mistake is writing rules that only check the source IP without also verifying the VLAN tag — which causes cross-segment bleed when two different subnets happen to use overlapping IPs (like 192.168.50.x on both a management and guest network). The tagged vmbr70 qualifier prevents this by ensuring that rule only matches traffic actually tagged with 70 at the bridge level, not just any packet arriving from subnet 192.168.50 regardless of which physical port or VLAN it came through.
When you're managing many VMs across clusters — especially if your setup has grown past a single node and involves automation like Automate Proxmox VE with Ansible Full VM Playbooks — consider using nftables rules defined at the cluster level rather than per-host, so VLAN scoping stays consistent across all nodes.
When Should I Layer in Additional Security?
Once your VLANs are segmented and tagged correctly, there's a natural progression from "separate networks" to "secure separate networks." Two tools that pair well with VLAN segmentation on Proxmox: fail2ban for per-VM brute-force protection (especially useful when you expose SSH or HTTP services) and CrowdSec if your cluster runs multiple hosts where shared ban lists matter.
Fail2ban is straightforward to configure on a single Proxmox host: install it, point the jail at your firewall's log file, set reasonable thresholds (I use 5 attempts in 30 minutes for SSH), and let nftables handle the actual blocking — which works well because fail2ban writes rules directly into the same chain that your VLAN-scoped traffic passes through.
For more complex setups where you're also managing Cloudflare Tunnel on Proxmox for zero-trust remote access, having proper VLAN segmentation means internal services aren't accidentally exposed to external clients when tunnel rules are being evaluated — a real-world gotcha I've seen cause intermittent connectivity issues during failover periods.
Practical Comparison: Single Bridge vs Segmented Networks
| Scenario | Best Approach | Why It Works Well Here |
|---|---|---|
| Homelab with 5-10 VMs, single switch port | Single bridge (vmbr0), pvid only | Simpler config; most services don't need isolation at Layer 2 |
| Multiple guest subnets on one uplink to managed switch | Bridge + bridge-tagged-vlans |
Preserves VLAN tags across the trunk without extra physical ports |
| Heavy storage traffic (iSCSI, NFS) alongside VMs | Separate bridge for storage network | Prevents broadcast storms from affecting management plane; see How to Set Up a Proxmox Cluster for multi-host considerations |
| Mixed LXC and KVM workloads with different VLAN needs | Per-bridge tagging + container vlan.id override |
Each workload type gets its own network identity without host config changes |
One Honest Tradeoff Worth Knowing
Adding VLANs to your Proxmox setup isn't free. Every tagged bridge adds a small amount of CPU overhead for processing 802.1Q headers (typically under 5% even on modest hardware), and it increases the number of rules in both nftables and any DHCP or DNS configuration downstream — which means more things that can go wrong when you're debugging connectivity issues at 3 AM.
For homelab setups, I'd recommend starting with a single bridge and only adding VLANs when you actually need them (like separating backup traffic from guest workloads). The rule of thumb: if your switch supports it and you have the cables for tagged ports on both ends, tagging is usually worth the small increase in configuration complexity. If not — or if your host has just one physical port to a basic unmanaged switch — don't force VLANs where they aren't needed yet.
Conclusion
VLAN tagging on Proxmox VE works through a combination of bridge-level pvid/tag properties, per-container overrides for LXC workloads, and nftables-scoped firewall rules that respect those tags at evaluation time — all without requiring additional physical interfaces or complex guest configuration when you don't need it. The real value emerges as your network grows: tagged bridges let you add more subnets over existing hardware before needing new cables or bonding upgrades, while VLAN-aware firewall rules prevent cross-segment bleed in multi-tenant setups.
The next step depends on where you're starting — if you have fewer than 10 VMs and a single managed switch port, start with bridge-pvid and experiment with adding one tagged bridge before committing to full segmentation; once your setup gets more complex, consider automating it with tools like Cockpit for visual management or Ansible playbooks if you're scaling across multiple hosts.