Skip to main content
glyph.sh
Type a command…⌘K

Home Network Segmentation: Isolating IoT and the Parts Everyone Skips

How to segment a home network with VLANs so a compromised IoT device can't reach your laptop. A practical trust-tier model, plus the gotchas guides skip: IPv6 that bypasses your v4 firewall, forced DNS, mDNS reflection for casting, and scoped remote access.

September 21, 2026 · 15 min read · VLAN · pfSense · IoT · Network Security · IPv6 · WireGuard · Homelab · DNS
Home Network Segmentation: Isolating IoT and the Parts Everyone Skips

Right now, your $12 smart plug can talk to your work laptop. So can the doorbell, the robot vacuum plotting its little routes, the TV that phones home more than a homesick freshman, and whatever your guests connected last weekend. They are all roommates on one flat network, and every one of them can knock on every other one’s door.

That is fine, right up until it is not. A smart bulb with a hardcoded password and firmware from 2019 gets popped, and suddenly the thing that turns your bedroom light on and off is doing a port scan of your file server. Nobody buys a light bulb expecting it to become a beachhead. That is exactly why it makes such a good one.

The fix is not another antivirus subscription. It is segmentation: taking one flat network and slicing it into zones that cannot freely reach each other, so a bad day for the bulb stays a bad day for the bulb. Enterprises do this as a matter of course. You can do it at home with a firewall, one managed switch, and one access point, in a weekend. Here is the model, and, more importantly, the handful of things that will absolutely bite you and that nobody warns you about.

Stop organizing by device. Organize by trust.

The instinct is to sort things by what they are: phones here, IoT there. Wrong axis. Sort them by how much you trust them, which usually means how much they would ruin your week if they turned on you. I use four zones, each its own VLAN:

  • Trusted. Laptops, phones, the machines you own, patch, and would be genuinely sad to lose. These get real access.
  • IoT. Anything with a cloud brain you did not write: TVs, cameras, plugs, speakers, the fridge that has opinions. Assume every one of them is already compromised, because someday one of them will be. Internet, and nothing else.
  • Guest. Other people’s devices. Internet only, and walled off from your IoT too, because your visitor’s phone has no business meeting your thermostat.
  • Lab. Where you break things on purpose. Kept in a padded room so the experiments stay in the experiment.

The whole game is default deny between zones. Traffic does not cross from one to another unless you wrote a rule that says it may. You are not playing whack-a-mole with bad packets. You are saying no to everything and then grudgingly saying yes to the few things you actually use.

Before any of that, though, each zone needs two things you have to choose: a VLAN ID and an address range. That is the un-fun bookkeeping, so I built a companion for it — the VLAN Calculator. Feed it a VLAN ID (I use 20 for Trusted, 30 for IoT, 40 for Guest, 50 for Lab), a name, and a rough device count, and it hands back a right-sized IPv4 subnet, the matching IPv6 /64, and the 802.1Q details to punch into your switch. Run it once per zone and you walk away with the exact address map the rest of this post assumes — no guessing at subnet masks, no accidentally overlapping ranges.

The rules, in plain English

Here is what each zone is allowed to do. I will use the documentation address ranges (192.0.2.0/24, 198.51.100.0/24) so none of this maps to a real network anywhere. When you build for real, drop in whatever the calculator sized for each zone — the rules below do not care about the specific numbers, only that yours do not overlap.

  • IoT gets the internet and a DNS resolver. That is the entire guest list. It cannot reach your firewall’s admin page, cannot see other VLANs, cannot start a conversation with your laptop. If the camera gets owned, it can still call its mothership, but it cannot turn around and rob the house.
  • Guest is the same deal, plus it is blocked from IoT. Company gets WiFi, not your light switches.
  • Trusted gets broad access, with one deliberate bit of favoritism: it can reach into IoT (so your phone can boss the TV around), but IoT cannot reach back. A stateful firewall makes this effortless. The replies to a conversation you started come back fine; a conversation the TV tries to start with you gets dropped at the door.
  • Lab is sealed off from all of it.

That asymmetry is the single most useful idea in this whole post: your stuff can touch their stuff, their stuff cannot touch yours. Read it twice. It is the shape of every good rule here.

A disclaimer, because this can absolutely hurt

The code below is illustrative, not a turnkey installer. There is no “paste this and you are done,” because firewalls, switches, and access points all speak different dialects, and your subnets, interface names, and gear are not mine. I use nftables syntax and documentation address ranges (192.0.2.0/24, 2001:db8::/32) so the logic is clear and nothing maps to a real network. On pfSense, OPNsense, OpenWrt, or VyOS the same rules live behind a GUI or a different config language, but the shape is identical.

So, the ground rules before you touch anything:

  • You will lock yourself out at least once. Have console or physical access to your firewall before you start. Not SSH. Physical.
  • Back up your config first, and know how to restore it from that console.
  • Rehearse on a spare box or a VM if you possibly can. A pfSense VM is free.
  • Assume every snippet here can take your whole network down if you paste it blind. Read each line, translate it to your platform, change the addresses. This is a “understand it, then adapt it” exercise, not a script to run at 2am.

We understand each other. Onward.

Now the parts that actually bite

Everything above is in every VLAN tutorial on the internet. These next five are the ones that separate a setup that genuinely holds from one that just looks segmented in a diagram. Each of these has personally cost me time, dignity, or both.

1. IPv6 will stroll right through your v4 firewall, whistling

This is the big one. The trap. You spend a beautiful evening crafting v4 isolation rules, you admire your work, you go to bed a security genius. Then your ISP quietly turns on IPv6, and every single rule you wrote stops applying, because IPv6 is a completely separate protocol with a completely separate rule set. Your “isolated” IoT VLAN now has a wide, sunny, unguarded IPv6 highway to everything you own.

Mirror every isolation rule onto IPv6. All of them. And when you write those v6 rules, point them at interface networks rather than a hardcoded prefix, because the prefix your ISP hands you can change, and a stale literal in a firewall rule is a rule that quietly fails open. One more gotcha inside the gotcha: do not block link-local (fe80::/10) in your isolation, because devices need it just to do basic IPv6 things. Block the routed traffic between zones, not the plumbing that makes IPv6 work at all.

If you remember one sentence from this entire post, make it this: dual-stack means dual rules.

The whole trap in two lines. The v4 rule you were so proud of:

# IoT -> Trusted, IPv4. Blocks the lateral move you were worried about.
ip  saddr 198.51.100.0/24  ip  daddr 192.0.2.0/24  drop

…does exactly nothing for IPv6 until you write it again, in the ip6 family, with your v6 prefixes:

# The same rule for IPv6. Without this line, IoT reaches Trusted over v6, freely.
ip6 saddr 2001:db8:0:2::/64  ip6 daddr 2001:db8:0:1::/64  drop

Then go prove it. From a device on the isolated VLAN, try to reach another zone, and specifically test v6, which is the half everyone forgets:

ping  -c1 192.0.2.10          # a Trusted host over v4  -> expect 100% loss
ping6 -c1 2001:db8:0:1::10    # the same over v6        -> expect 100% loss

If either one succeeds, or your firewall’s drop counter does not tick up, you have a hole. Find it before something else does.

2. Forced DNS, or your filtering is a coat of paint

You point your network at a nice filtering resolver. You feel responsible. Then a smart TV, which has never respected a setting in its life, hardcodes 8.8.8.8 and ignores everything you configured. Now it has DNS you cannot see, cannot filter, and cannot log, and if it speaks DNS-over-TLS it can tunnel its lookups right out the back while you wave at the front door.

Two rules end this. First, redirect all outbound port 53 back to your own resolver, so a device demanding external DNS transparently gets yours whether it likes it or not. Second, block outbound port 853 so it cannot encrypt its way around you.

# In your nat/prerouting chain: transparently hijack all IoT DNS to your resolver.
iifname "iot" udp dport 53 dnat to 192.0.2.1
iifname "iot" tcp dport 53 dnat to 192.0.2.1

# In your filter/forward chain: kill the DNS-over-TLS escape hatch.
iifname "iot" tcp dport 853 drop

Together, every name lookup on your network funnels through one resolver you actually control. Without this, DNS filtering is decorative. It is a “beware of dog” sign with no dog. (Do the same for iifname "guest", and yes, mirror the redirect for IPv6 too, because we just learned that lesson the hard way.)

3. Isolation kills casting, so reflect the introductions

The very first thing you will notice after isolating IoT is that casting dies. AirPlay, Chromecast, all of it, stone dead. That is because they find each other using multicast DNS, and multicast does not cross subnet lines. Your phone on Trusted literally cannot hear the TV on IoT anymore. It is right there. It cannot see it.

The tempting fix is to punch a hole in the firewall between the two zones. Do not do that. Use an mDNS reflector instead. It copies just the little “hi, I exist, here is what I can do” announcements across the boundary, so your phone can discover the TV, while your firewall still decides whether the actual stream is allowed. You are forwarding the introductions, not handing out keys. Discovery and access are two different problems, and the reflector lets you solve them separately, which is the entire trick.

avahi is the usual tool, and the config is refreshingly boring. Reflect between only the two interfaces that need it, not everything:

# /etc/avahi/avahi-daemon.conf
[server]
allow-interfaces=trusted,iot   # only these two zones see each other's mDNS

[reflector]
enable-reflector=yes

That is the whole feature. Your phone on Trusted can now find the speaker on IoT, and your firewall rules still decide whether the audio is actually allowed to flow. Do not add Guest to that list unless you enjoy your visitors discovering your television.

4. The stale DHCP lease that ate an hour of my life

You move a device to its shiny new VLAN. It half-works. It has an address, but it is the old address from the old VLAN, so nothing routes, and you stand there confused. What happened: the device is clinging to its previous DHCP lease like a toddler to a blanket, and depending on how it renews, your DHCP server may cheerfully hand the wrong subnet’s address right back to it.

When you re-home a device, force a fresh lease. Release and renew on the client, or delete the old lease on the server so it cannot be handed back out:

# Linux
sudo dhclient -r eth0 && sudo dhclient eth0
# macOS
sudo ipconfig set en0 NONE && sudo ipconfig set en0 DHCP
# Windows
ipconfig /release && ipconfig /renew

A reboot or a cable re-plug often is not enough on its own, which is precisely the part that will have you questioning your career choice for a solid hour. Ask me how I know.

5. Do not lock yourself out of your own house

When you convert your switch and access point to carry tagged VLANs, it is genuinely easy to sever the exact connection you are using to configure them, live, mid-command, with no way back in except a walk to the closet and a factory reset. Keep your management machine on a port that stays on the native management VLAN the whole time, so flipping a trunk does not cut your own lifeline. Always configure with a way back in. This is the networking equivalent of not painting yourself into the corner, and the number of people who have painted themselves into that corner is, statistically, everyone. Once.

Remote access, but on a leash

You will want to reach home while you are out. The lazy move is a VPN that dumps you onto the whole network like you never left. Do better. Put it on a leash.

WireGuard is the right tool, and the decisions that matter are all about blast radius, not the tunnel. Give the remote client access to only the zones it actually needs, which for most people is your Trusted devices and nothing else. Not IoT, not Guest, not Lab, and think hard before you add your management plane to that list. Your firewall’s admin page, your switch, your hypervisor: that is the most valuable thing on the network, and wiring a VPN straight to it means a leaked key owns everything, not just your laptop. If you need to administer the network remotely, reach a Trusted host first and jump from there, so the control plane sits one deliberate step behind the tunnel instead of directly on it. If that key ever leaks, the intruder should land in a small, boring room, not the server closet. Run it split-tunnel so only home-bound traffic takes the tunnel and your regular browsing stays fast and local. Park the endpoint on a forgettable hostname and a non-default port. WireGuard does not even reply to a packet without the right key, so a scanner cannot confirm it is there at all, and the obscurity just keeps it out of the automated noise.

The one line that matters is AllowedIPs. This is the whole leash:

[Interface]
PrivateKey = <this device's private key>
Address    = 10.10.10.2/32
DNS        = 10.10.10.1

[Peer]
PublicKey  = <server public key>
Endpoint   = something-forgettable.example.com:51820
# The leash. Only your Trusted subnet + the tunnel itself.
# NOT 0.0.0.0/0, NOT IoT or Guest, and NOT your management network.
# If this key leaks, the blast radius is your daily devices, not the firewall.
AllowedIPs = 192.0.2.0/24, 10.10.10.0/24
PersistentKeepalive = 25

The rule that governs your paranoid IoT VLAN governs your own front door too: least privilege. Give the minimum, even to yourself, especially to the version of yourself whose phone might get stolen.

Automating the tedious part

Hand-writing isolation rules gets old fast, because the count grows with the square of your zones. Four zones is twelve directed pairs; you are not typing those by hand and you are definitely not keeping them consistent across v4 and v6 by hand. So describe your zones once and generate the drop rules from that, both address families, every pair that is not explicitly allowed:

#!/usr/bin/env bash
# Generate default-deny inter-zone drop rules (v4 + v6) from a zone map.
# ILLUSTRATIVE. Read every generated line before you load it. This can, and
# will, cut your network if you feed it wrong addresses. Documentation ranges
# used throughout; swap in yours.
set -euo pipefail

# zone -> v4 subnet
declare -A NET4=( [trusted]=192.0.2.0/24 [iot]=198.51.100.0/24 [guest]=203.0.113.0/24 [lab]=192.0.2.128/25 )
# zone -> v6 prefix (mirror, because dual-stack means dual rules)
declare -A NET6=( [trusted]=2001:db8:0:1::/64 [iot]=2001:db8:0:2::/64 [guest]=2001:db8:0:3::/64 [lab]=2001:db8:0:4::/64 )

# One-way flows you WANT to keep open: "src>dst" (stateful, so replies come back)
ALLOW=" trusted>iot trusted>lab "

for src in "${!NET4[@]}"; do
  for dst in "${!NET4[@]}"; do
    [ "$src" = "$dst" ] && continue
    case "$ALLOW" in *" $src>$dst "*) continue ;; esac   # skip the flows we allow
    echo "ip  saddr ${NET4[$src]}  ip  daddr ${NET4[$dst]}  drop  # $src -> $dst (v4)"
    echo "ip6 saddr ${NET6[$src]}  ip6 daddr ${NET6[$dst]}  drop  # $src -> $dst (v6)"
  done
done

Run it, read the output, and then decide what to do with it. It prints the rules; it does not load them, on purpose, because a script that silently reconfigures your firewall is a script that silently locks you out. The value is that the v4 and v6 rules are generated from the same source of truth, so they can never drift apart, which is exactly the failure mode from gotcha number one. The NET4 and NET6 maps are the one place real numbers belong, and they line up one-to-one with the calculator’s per-zone output: its IPv4 subnet becomes a NET4 entry, its IPv6 /64 becomes the NET6 mirror. Fill the map from there and your dual-stack rules stay in sync by construction.

This is the honest ceiling on automation here: you can generate the repetitive rules, but you cannot automate away understanding your own network. Anyone selling you a one-click “secure home network” button is selling you a false sense of one. The point of doing it yourself is that afterward, you actually know what your network does.

The whole philosophy on one napkin

None of this needs a rack or a budget. A capable firewall, one managed switch, and one access point that can map a few SSIDs to VLANs will get you there. The build is a weekend. Most of that weekend is learning, and the learning is the point.

What actually matters is the posture:

  • Default deny. Nothing crosses zones unless you meant it.
  • Assume breach. Design so a compromised device is a contained annoyance, not a housefire. Your IoT VLAN exists because you fully expect something in it to get owned eventually.
  • Least privilege everywhere, your own remote access included.
  • And the three that everyone forgets: dual-stack means dual rules, forced DNS is not optional, and discovery is not the same thing as access.

Segmentation does not make your network unbreakable. Nothing does. What it buys you is that when something breaks, and something always eventually breaks, the wreckage stops at the wall of one zone instead of spreading to everything you care about. The smart bulb is allowed to have a terrible day. It is just not allowed to take your laptop down with it.