VLANs Without Fear: Segmenting a Home Network That Won't Bite Back

How to carve one flat home LAN into isolated zones without locking yourself out of your own router

Contents

Most home networks are one flat sheet of ice. Everything — the laptop, the phones, the smart plug you bought on a whim, the NAS with the family photos, the cheap IP camera whose firmware was last updated during a different presidency — sits on the same subnet, able to talk to everything else with no supervision. That works right up until it doesn’t, and the day it stops working is usually the day one of those devices turns out to have a listening service it shouldn’t, and now the thing with the family photos is reachable from the thing running unpatched firmware.

VLANs are how you stop that. A VLAN — virtual LAN — lets a single switch and a single router behave as though they were several separate switches and routers, with traffic between the segments passing through a firewall you control. The camera goes in one box, the NAS in another, and the only way from one to the other is a rule you wrote deliberately. The reason people avoid VLANs is not that they’re conceptually hard. It’s the very real fear of tagging the wrong port, cutting the link between your management laptop and the router’s web interface, and being left staring at a device you can no longer reach. This is a guide to doing it without that happening.

Why segment at all

Advertisement

The honest case for segmentation is blast radius. On a flat network, one compromised device sees every other device. Ransomware that lands on a laptop can enumerate SMB shares across the whole subnet. A botnet-recruited camera can port-scan your NAS at its leisure. Nothing stops lateral movement because there is nothing in the middle to stop it — every host is one ARP request away from every other host.

Put the camera on an IoT VLAN with a firewall rule that says “this segment may reach the internet and nothing else on the LAN,” and the picture changes. If that camera is compromised, the attacker gets a camera and a route to the internet, which they already had. They do not get your NAS, because the packets they’d need to send never make it past the router’s rule set. Segmentation converts a flat, fully-connected graph into a small number of zones with narrow, audited doorways between them.

There’s a quieter benefit too: broadcast containment. Every device on a subnet hears every broadcast on that subnet — ARP, mDNS, DHCP, the lot. On a network with sixty IoT gadgets all chattering, that background noise is measurable. Splitting them into separate broadcast domains keeps each segment’s noise to itself, which matters more than you’d think once you start counting devices.

The vocabulary you actually need

Three terms carry almost all the weight, and getting them straight up front prevents most of the lock-out disasters.

An access port carries traffic for exactly one VLAN, untagged. This is what you plug an ordinary device into — a laptop, a printer, a games console. The device has no idea VLANs exist; the switch quietly associates whatever arrives on that port with, say, VLAN 20. The device sends plain Ethernet frames and receives plain Ethernet frames. All the VLAN machinery happens on the switch side of the cable.

A trunk port carries traffic for multiple VLANs, and it tells them apart by adding a small tag to each frame — the 802.1Q header, four bytes that include a 12-bit VLAN ID. Trunks are the links between VLAN-aware equipment: switch to switch, or switch to router. A frame leaving a trunk for VLAN 20 has a “20” stamped inside it; the device at the other end reads that stamp and knows which VLAN the frame belongs to.

The native VLAN (or PVID, depending on your switch’s vocabulary) is the VLAN that untagged frames on a trunk get assigned to. This is the single most common source of self-inflicted lock-outs. If your management interface expects VLAN 1 untagged and you change a trunk’s native VLAN to something else, your management traffic silently vanishes into a VLAN nothing is listening on. Write down the native VLAN of every trunk before you touch anything.

The router does the last job: inter-VLAN routing. VLANs on their own are isolated at layer 2 — a device in VLAN 20 cannot even ARP for a device in VLAN 30, because they’re separate broadcast domains. To get a packet from one to the other, it has to go up to a device with an interface in both VLANs, get routed, and (crucially) get filtered on the way. That device is your router or layer-3 switch, and the filtering is where all the security value lives.

A worked layout

Advertisement

Here’s a segmentation that covers most homes without becoming a part-time job to maintain. Four VLANs, each a /24, each with an obvious purpose. I’m using mylab.local and RFC1918 space throughout; substitute your own.

VLANPurposeSubnetGateway
10Management (switches, router, APs)192.168.10.0/24192.168.10.1
20Trusted (laptops, phones, workstations)192.168.20.0/24192.168.20.1
30IoT (cameras, plugs, TVs)192.168.30.0/24192.168.30.1
40Servers (NAS, homelab containers)192.168.40.0/24192.168.40.1

The management VLAN is deliberately first and deliberately sparse. The only things on it are the network devices themselves and, when you’re configuring them, one trusted machine. Keeping the switch and router admin interfaces off the VLAN where everyday laptops live means a compromised laptop can’t reach the router’s login page to begin with.

On a Linux router (or an OPNsense/pfSense box, or a managed switch doing layer 3), the interface configuration for a router-on-a-stick setup looks roughly like this — one physical NIC carrying all four VLANs as tagged sub-interfaces on a trunk to the switch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# /etc/network/interfaces (Debian-style), router-on-a-stick on eth1
auto eth1
iface eth1 inet manual

# Management VLAN 10
auto eth1.10
iface eth1.10 inet static
    address 192.168.10.1/24

# Trusted VLAN 20
auto eth1.20
iface eth1.20 inet static
    address 192.168.20.1/24

# IoT VLAN 30
auto eth1.30
iface eth1.30 inet static
    address 192.168.30.1/24

# Servers VLAN 40
auto eth1.40
iface eth1.40 inet static
    address 192.168.40.1/24

The eth1.10 naming is the 802.1Q sub-interface convention: the parent NIC is eth1, the number after the dot is the VLAN ID, and the kernel handles tagging automatically. Each sub-interface becomes the default gateway for its VLAN. The switch port this NIC connects to must be configured as a trunk carrying VLANs 10, 20, 30 and 40 tagged.

The firewall rules are the whole point

Sub-interfaces and trunks move packets around; they enforce nothing. Without firewall rules, inter-VLAN routing means every VLAN can reach every other VLAN, which gives you the noise-containment benefit and none of the security benefit. The rules are what earn the effort.

The policy I’d start with, expressed in plain English before any syntax:

  • Management (10) can reach everything. It’s where you administer from.
  • Trusted (20) can reach the internet, the servers (40), and can initiate connections to IoT (30) so your phone can talk to a smart plug.
  • IoT (30) can reach the internet and nothing else on the LAN. It cannot start conversations with Trusted or Servers.
  • Servers (40) can reach the internet for updates and can reply to Trusted, but cannot initiate connections into Trusted or Management.

Rendered as nftables on a Linux router, the interesting part is the forward chain. Established and related traffic is allowed first so replies flow, then specific allowances, then a default drop:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
table inet filter {
    chain forward {
        type filter hook forward priority 0; policy drop;

        # Allow return traffic for any connection we permitted
        ct state established,related accept

        # Management can go anywhere
        iifname "eth1.10" accept

        # Trusted -> internet, servers, and IoT
        iifname "eth1.20" oifname "eth0" accept          # internet (eth0 = WAN)
        iifname "eth1.20" oifname "eth1.40" accept        # -> servers
        iifname "eth1.20" oifname "eth1.30" accept        # -> IoT (initiate only)

        # IoT -> internet only, never lateral
        iifname "eth1.30" oifname "eth0" accept

        # Servers -> internet only (replies handled by ct state above)
        iifname "eth1.40" oifname "eth0" accept

        # Everything else is dropped by policy
    }
}

The ct state established,related accept line is doing quiet heavy lifting. It’s why IoT can reply to a connection Trusted started, while still being unable to start one of its own — the return packets match an existing connection-tracking entry and sail through, but an unsolicited packet from IoT to Trusted matches no entry and hits the default drop. Stateful filtering is the difference between “these two can have a conversation the trusted side began” and “these two can freely reach each other.”

Doing it without locking yourself out

Every VLAN lock-out story has the same shape: someone changes a trunk or native VLAN on the very link they’re managing the device through, applies it, and the session dies. Here’s how to make that survivable.

Keep a wired console path. Most managed switches have a serial console or an out-of-band port that doesn’t care about your VLAN config. If your only path to the switch is its web UI over the network you’re reconfiguring, you’re one typo from a factory reset. Know where the console cable is before you start.

Change the far end first. When setting up a trunk between switch and router, configure the router side, then the switch side. If you tag the switch port before the router is ready, you’ve cut the link with nothing on the other end to restore it from.

Use a commit-confirm or scheduled rollback if your gear supports it. OPNsense and pfSense will roll back a firewall change automatically if you don’t confirm it within a timeout. Some switches have reload in 5 — a scheduled reboot you cancel once you’ve verified connectivity. If the change locks you out, the box reverts on its own.

Test from a device on the VLAN you think you configured, not from your assumptions. After bringing up VLAN 30, plug a laptop into an access port assigned to VLAN 30, confirm it gets a 192.168.30.x address from DHCP, confirm it can reach the internet, and confirm it cannot reach 192.168.40.x. That last test is the one people skip, and it’s the one that proves the isolation is real rather than merely configured.

Troubleshooting

Advertisement

A device on an access port gets no IP. The switch port’s PVID (access VLAN) probably doesn’t match the VLAN your DHCP server serves. Confirm the port is an access port for, say, VLAN 30, and that a DHCP scope exists for 192.168.30.0/24 reachable from that VLAN. If the DHCP server lives on the router’s eth1.30 interface, it’s local; if it’s a separate server on another VLAN, you need a DHCP relay pointing at it.

Devices get an IP but can’t reach the internet. Almost always a missing rule allowing that VLAN out to the WAN interface, or missing NAT/masquerade for that subnet. Check that the VLAN’s subnet is included in your outbound NAT rules — a new VLAN’s traffic won’t be translated to your public IP unless you tell the router to translate it.

Two VLANs can reach each other when they shouldn’t. Your default forward policy is accept instead of drop, or a broad allow rule is matching before your specific deny. Rule order matters: firewalls evaluate top to bottom and stop at the first match. Put the default drop as policy, allow explicitly, and never lead with a permissive rule you meant to narrow later.

mDNS/AirPlay/Chromecast stops working across VLANs. This is working as designed — discovery protocols rely on broadcast and multicast that don’t cross VLAN boundaries. If you want the trusted VLAN to cast to a speaker on the IoT VLAN, you need an mDNS repeater (Avahi in reflector mode, or the equivalent built into OPNsense/pfSense) to forward those announcements across the boundary, plus a firewall rule permitting the actual media stream. Expect to spend an evening on this; it’s the single most annoying part of home segmentation.

Management interface unreachable after a trunk change. Native VLAN mismatch, nine times out of ten. The trunk’s native VLAN no longer matches where your management traffic expects to be untagged. This is where the console cable earns its keep.

Is it worth it?

For a household with a laptop and a phone, honestly, no — a flat network is fine and segmentation is complexity you’ll resent. The moment it becomes worthwhile is when you add devices you don’t trust: cheap IoT gadgets, a media server exposed to guests, cameras, anything running firmware you can’t update. Once “things I’d rather keep away from my important data” outnumber “things I trust completely,” the blast-radius argument wins and the evening of setup pays for itself.

If you’re already running a homelab, VLANs are close to mandatory — they’re the substrate everything else sits on. A guest network worth the name needs its own segment; giving containers their own addressing gets far cleaner once you understand tagging, which I get into in MACVLAN and IPVLAN: giving containers real IPs; and running a DNS sinkhole per-segment is trivial once the segments exist, which the AdGuard Home vs Pi-hole shoot-out covers. Start with two VLANs — trusted and IoT — get the isolation test passing, and grow from there. The fear is real, but it’s a fear of one specific mistake, and that mistake has a console cable for an antidote.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.