Home Automation - Segmenting Home Networks: AV and IoT on Separate VLANs
The Question: Is it possible to segment AV and IoT onto Separate VLANs (away from your laptop) at Home?
Every guide I’ve read about segmenting a smart home ends up giving up at one point, and making the same two errors (or concessions if you’re kind). Put your Apple TVs and HomePods on the trusted network with your laptops, and don’t even try to move Sonos. Here’s a fairly typical example, which calls Sonos “the ultimate exception to every networking rule”.
I’ve just rebuilt our network with a houseful of Sonos units, Apple TVs, HomePods and Chromecasts on their own AV VLAN, away from both the laptops and the IoT gear. AirPlay works, Sonos grouping works, HomeKit works, and nobody in the house has noticed anything.
A very long time ago I was a sysadmin who also worked on low-level network code at a wholesale ISP. That meant FreeRADIUS for a few hundred thousand customers, maintaining our internal fork of the l2tpns C code for a >200k-subscriber 3G network, and an enormous amount of time staring at tcpdump. Unlike a large proportion of people running homelabs (who are trying to learn enterprise technology), I have ZERO interest in having a second job at home. I’ve tried as hard as possible to keep the below approach as simple as possible. But I wasn’t going to accept “it can’t be done” when the real problem is the wrong design and that nobody has reached for the right tool.
Introduction
The thing that makes everyone give up on segmenting their network is service discovery. A home network runs a pile of (often poorly-designed) protocols that assume every device is dumped into the one network - i.e. one flat broadcast domain:
- mDNS on UDP 5353 (AirPlay, HomeKit, Chromecast, newer Sonos, AirPrint)
- SSDP on UDP 1900 (Older Sonos, DLNA, various media devices)
- Raw UDP broadcast (on whatever random port the vendor felt like - e.g. iZone on 12107, Tuya on 6666 & 6667, Big Ass Fans on 31415)
- IPv6 link-local multicast (Matter commissioning and Thread)
A VLAN breaks all of them. Typical gateways can relay only one of them (mDNS), and to be fair, UniFi’s mDNS proxy does a pretty good job - for the part of your network that speaks mDNS, it mostly works.
Anyway, people trying to segment their network soon notice that reflection is flaky, and wrongly conclude that the devices have to live together.
The trick is that you don’t need discovery to cross a boundary if the thing doing the discovering is already sitting on both sides.
My Home Assistant instance doesn’t live on one VLAN. It’s a Docker container with three macvlan interfaces, one each on Servers, AV and IoT. Each leg gets its own address on that subnet, so HA sees broadcast and multicast traffic natively on all three of them. No reflection, no relay, and no need to care that iZone’s discovery protocol was apparently written by someone who’d never heard of a router.
Why Bother?
The usual reason for segmenting is that cheap IoT gear never gets security updates and is a security risk to everything else on your network. Definitely true, but there’s a second reason that I think is going to be far more important over the next few years.
Try counting the devices on your network. Ours is well north of a hundred, and nobody has been trying especially hard to get it there. We have speakers, streaming boxes, cameras, inverters, air conditioners, a heat pump, a mower, cars, door locks, an oven, and a pile of sensors. A /24 holds 254 hosts. We’re probably one renovation away from running out.
The answer to that isn’t a bigger subnet. Every one of those devices is ARPing, broadcasting, and announcing itself over mDNS. Put several hundred of them on a flat /16 and you’ll have a permanent broadcast storm. It’s worse again over wireless, because multicast goes out constantly, chewing up airtime that something else needs. Segmenting isn’t just about security — it’s keeping broadcast domains small enough to actually work.
Requirements
- A UniFi gateway that can run the Zone-Based Firewall. Network 10 uses ZBF and nothing else, and it needs gateway firmware 4.1 or newer. If yours is older and EOL, unchecking “Allow Internet Access” on a network will generate no firewall rules whatsoever (I discovered this one the hard way).
- Home Assistant (or Homebridge, or whatever your controller is) in Docker, so you can give it more than one interface (or figure this part out yourself via a different mechanism).
- The patience to re-onboard every IoT device through its vendor app. This frankly sucks. The vendor apps are all awful (TuYa is particularly terrible), and there’s no easy way around it.
The VLANs
| VLAN | Name | Subnet | Zone | Contents |
|---|---|---|---|---|
| 1 | Mgmt | (the old flat network) | Internal | UniFi kit, network management |
| 100 | Servers | 10.42.100.0/24 | Internal | Containers, VMs |
| 110 | Trusted | 10.42.110.0/24 | Internal | Phones, laptops, iPads |
| 120 | AV | 10.42.120.0/24 | AV | Apple TVs, HomePods, Sonos, Cast |
| 130 | IoT | 10.42.130.0/24 | IoT | Everything cheap with a Chinese cloud |
| 140 | Cameras | 10.42.140.0/24 | Cameras | UniFi Protect cameras |
| 150 | Guest | 10.42.150.0/24 | Hotspot | Guests |
The 42 there is arbitrary — pick your own unusual second octet. Don’t use 10.0.x.0/24 or 192.168.x.0/24, because they’re the default on a huge number of routers. The next time you VPN home from a hotel whose LAN happens to be 192.168.0.0/24, your route to the home network is ambiguous and you will have weird problems.
Group Devices by Egress, Not by Function
I got this wrong initially. My first plan grouped things by what they are — media devices together, IoT devices together.
That’s mostly correct in practice, but backwards. Nearly everything on the AV VLAN needs unrestricted internet, so putting it behind a default-deny policy achieves nothing except an allow-list to maintain. Meanwhile the one device in that group I actually don’t trust — a smart TV that we only use as a dumb panel for an Apple TV, and which phones home constantly (to be clear, I love the Art Frame, but I loathe the software) — was sitting on the permissive segment.
So group by egress posture instead:
- IoT is default-deny, with an explicit allow-list. Anything I add to that VLAN gets no internet until I say so.
- AV gets unrestricted egress. The point of that VLAN is lateral containment, not egress control.
- Cameras get no internet at all, with one explicit allow to the NVR.
Why does this matter? Because of which way the failures point. If you forget an allow rule, the device stops working and you’ll know about it within a day. If you forget a deny rule, nothing happens, forever. Putting the untrusted devices on the default-deny segment means that when you make a mistake, the mistake is safe.
UniFi has a built-in zone called No Internet by Default which does exactly this. Use it rather than rolling your own zone with a deny rule at the bottom that somebody will reorder in six months.
Two VLANs on One SSID
The usual objection to a separate AV VLAN is that SSID determines VLAN, so every segment needs its own SSID, and nobody wants five networks cluttering up the wifi list on their phone.
Private Pre-Shared Keys (PPSK) get around this. One SSID carries several passphrases, and each one maps to a different VLAN:
| SSID | VLANs | Security |
|---|---|---|
| FBIVan | 110 | WPA2/WPA3, PMF on |
| FBIVan-AV-IoT | 120 or 130, by key | WPA2 + PPSK, PMF off |
| FBIVan-Guest | 150 | Guest network type |
PPSK does force WPA2 on that SSID, so no WPA3 and no 6 GHz. On an SSID that’s carrying smart plugs and Sonos units, neither of which can do WPA3 or 6 GHz anyway, that costs you nothing. The trusted SSID keeps WPA3, because PPSK is only on the SSID that didn’t want it.
Use the Guest network type for the guest SSID rather than a normal WLAN tagged to that VLAN. Client isolation comes from the network type and not from the VLAN, which is easy to assume otherwise.
What I Tried First
RADIUS MAC authentication first seemed like a better solution. You could then assign VLANs server-side by MAC address and never touch a device. Given I’ve a bit of experience with RADIUS, I assumed this would be the easy bit.
It turns out UniFi’s implementation is deny-by-default. An unenrolled MAC gets an Access-Reject and simply can’t associate, rather than falling through to the WLAN’s own network. iOS reports this as an incorrect password, so you go looking in completely the wrong place for a while.
Sadly, this makes it no solution at all. I would have needed to enrol every device rather than just the exceptions, and enrolling Apple devices means enrolling their randomised per-SSID addresses, which change if the setting gets toggled or the device is reset. Too high a risk of problems down the track.
PPSK gets you most of what MAC auth was for, and lets us run one less service.
Multi-homing the Controller
Here’s the bit that actually makes it work. I use Docker, but the concept is the same. Create a macvlan network per VLAN, against the VLAN sub-interfaces on the Docker host:
docker network create -d macvlan \
--subnet=10.42.100.0/24 --gateway=10.42.100.1 \
-o parent=ens19 vlan100
docker network create -d macvlan \
--subnet=10.42.120.0/24 \
-o parent=ens20 vlan120
docker network create -d macvlan \
--subnet=10.42.130.0/24 \
-o parent=ens21 vlan130
Note that only vlan100 gets a --gateway. This is deliberate, and it took me
an hour to work out the first time. Docker installs a default route for any
macvlan network that has a gateway, so with three legs you get a race over
which one wins. Mine landed on the IoT VLAN, which meant Home Assistant’s
traffic to the NVR was being evaluated by the firewall as IoT→Internal and
silently dropped. The symptoms made no sense at all until I ran ip route
inside the container.
The Compose side is boring:
homeassistant:
container_name: homeassistant
image: "ghcr.io/home-assistant/home-assistant:stable"
networks:
vlan100:
ipv4_address: 10.42.100.10 # servers: MQTT, database
vlan120:
ipv4_address: 10.42.120.10 # AV: Sonos, Apple TV, Cast
vlan130:
ipv4_address: 10.42.130.10 # IoT: Meross, Tuya, SolarEdge, Roborock
networks:
vlan100:
external: true
vlan120:
external: true
vlan130:
external: true
Check it rather than assuming:
docker exec homeassistant ip route | grep default
You want one default route, via the gateway on your server VLAN.
Homebridge gets the same treatment, because it’s running my iZone air conditioning plugin. iZone finds its bridge by UDP broadcast on port 12107, and UniFi can’t relay that, as there’s no general-purpose broadcast relay anywhere in the product. A macvlan leg on the IoT VLAN trivially fixes it, when no amount of firewall configuration would ever have.
One gotcha: a Linux host can’t reach its own macvlan children over the parent interface. If the Docker host also needs to talk to these containers, give it a separate interface on a different subnet and let the traffic route via the gateway. I hit this trying to reach Pi-hole from the host and spent a while convinced it was the firewall.
Last thing — in general, for multi-homed controllers, you may need to pin which interface it advertises on. Otherwise it advertises on all of them, and ‘weirdness’ may occur:
homekit:
- advertise_ip: 10.42.120.23
Homebridge has the same thing in config.json:
"bridge": { "bind": ["10.42.120.22"] }
Skip this and you get accessories that work perfectly for a week and then go unresponsive after something restarts (i.e. a blackout).
mDNS: Custom Not Auto
UniFi’s mDNS proxy has three modes, and Auto retransmits across every VLAN. That quietly pushes discovery traffic into your IoT, camera and guest segments and undoes a good chunk of what you’ve just built. Use Custom and scope it:
| Service | Scope |
|---|---|
_airplay._tcp, _raop._tcp |
Trusted, AV |
_sonos._tcp |
Trusted, AV |
_googlecast._tcp |
Trusted, AV |
_hap._tcp |
Trusted, AV |
_ipp._tcp, _ipps._tcp |
Trusted (the printer lives there) |
Also note that mDNS Proxy is on by default on any newly created network, so you need to turn it off explicitly on IoT, Cameras and Guest. It is not off until you’ve gone and checked.
AirPrint is worth a mention. iOS has no way to add a printer by IP — it’s
Bonjour discovery or nothing. So either you reflect _ipp._tcp into whichever
VLAN the printer is on, or you put the printer on Trusted and deny it internet
individually. I did the latter, which keeps the mDNS scope tight, and if my
printer works and has no internet connection, I don’t actually want it getting
updates (e.g. HP has a notorious habit of bricking printers or blocking them from printing because HP
feels it suddenly wants more money). Do be
aware that denying egress does nothing about lateral movement, because
intra-VLAN traffic never reaches the gateway. That’s an accepted risk rather
than a fix.
Where I Disagree About IGMP Snooping
The article I first linked to says to turn IGMP snooping off, because UniFi’s implementation drops the multicast discovery packets that HomePods and Apple TVs rely on.
I’ve got it on for the trusted and AV VLANs, with the gateway as querier, and Sonos grouping, AirPlay and HomeKit all work properly. A houseful of Sonos units generates an impressive amount of multicast, and letting it flood both segments is real airtime on the wireless side.
To be clear, I’m still not 100% sure why this recommendation was made, as our network config does differ in a few ways. If your AV discovery is flaky then turning snooping off is a sensible thing to try. But “turn it off” isn’t universal advice, and on a network with a lot of multicast sources the flooding is not free.
I’d also suggest that you turn on Multicast Filtering on the trusted SSID. Access points will otherwise forward all wired multicast to every wireless client.
Zone-Based Firewall Gotchas
Two things here cost me quite a bit of debugging time:
Stateful doesn’t mean bidirectional. Return traffic on an allowed flow passes without a rule, which is what you’d expect. But a connection initiated from the other side is a new flow and needs its own policy. My cameras could reach the NVR perfectly well, the NVR couldn’t reach the cameras, and Protect showed every camera offline. Camera-to-NVR needs rules in both directions.
First match wins, top-down, within a zone pair. Allows have to be reordered above the built-in blocks. The matrix cell shows the last rule in the chain rather than the effective policy, so a cell that reads “Allow All” might be sitting underneath three blocks that catch everything first. Click into the cell and read the actual list.
And one that was entirely my own fault, which I’ll write down so somebody else doesn’t repeat it. I created a policy called “Allow Cloud Key to Cameras” with the Action accidentally and incorrectly set to Block. The name read like the intent so I looked everywhere else first. Sort by the Action column after you create a batch of rules, because a mislabelled action is otherwise invisible.
Also don’t bother specifying ports for Protect. It uses 7442–7447 plus UDP discovery, and getting one of them wrong gives you a silent partial failure rather than an obvious one. It’s a single host, so allow all protocols between the camera network and the NVR and tighten it up later if you care.
Things That Can’t Cross a VLAN
Most guides skip explaining this, so for completeness:
Raw UDP broadcast. UniFi’s proxy does mDNS only. There’s no
ip helper-address equivalent and no general broadcast relay. iZone, Jellyfin,
Tuya discovery, Big Ass Fans — none of these will ever cross a boundary in
UniFi. Multi-home the controller, or put the device on the controller’s
segment. Those are the options.
Matter commissioning. It uses IPv6 link-local multicast, which reflection can’t carry. The commissioning phone, the device and the Matter controller all need to be on the same subnet. If you commission through Home Assistant rather than Apple Home then matter-server is the commissioner and your phone is only relaying the BLE handshake, which sidesteps it nicely. If you commission through Apple Home then the iPhone is the commissioner and has to be on the device’s VLAN while you do it. I’m not running Matter yet, so treat that as reasoning from the protocol rather than something I’ve tested.
Vendor apps with broadcast-only discovery. The iZone iOS app finds its bridge by broadcast. If you need the vendor app rather than HA or HomeKit then that bridge has to sit on the same segment as your phone. That’s a real constraint, and the honest answer is to put that one device on Trusted with egress denied rather than pretend otherwise.
The Obvious Downside
A multi-homed controller is a bridge between every segment it touches. If you compromise my Home Assistant container, you’ve got a foot on Servers, AV and IoT at the same time, with the firewall policy bypassed entirely, because that traffic never crosses a zone boundary for the gateway to look at. Same goes for Homebridge and its leg on IoT.
That’s a true reduction in what the segmentation buys you. This isn’t a corporate or datacentre network, however, and the purpose isn’t to have the world’s strictest security policy. After all, the only way to have a secure computer is to switch it off, fill the case with concrete, and bury it.
The reasons why this design is still supportable:
- Home Assistant has to reach every segment to do its job. Whether it does that through three interfaces or through a pile of firewall allow rules, it ends up with the same reach either way. At least the compromise is visible in the Docker Compose file.
- Home Assistant is much better maintained software than anything it’s talking to. If something on my network is going to be the vulnerability to get in, a $20 Tuya plug is more likely the concern.
- Containers that don’t need multiple legs don’t get them. Nothing on the trusted VLAN is reachable from a compromised HA except through the firewall, because HA has no leg on Trusted.
Testing
Check each of these rather than assuming:
- AirPlay from a phone on Trusted to a Sonos on AV
- Sonos app discovery and grouping — group three or four units, not just two
- A HomeKit accessory from Homebridge, after restarting the Apple TVs
- Chromecast from a laptop
- Protect showing all cameras online, which exercises both directions of the camera rules
- AirPrint from an iPhone
Then unplug something on the IoT VLAN and make sure it comes back without internet access, so you know the default-deny is actually in force rather than sitting underneath an allow you’d forgotten about.
The Bottom Line
You can give up on segmentation if you really want. It’ll probably still work fine for now, although the amount of multicast will probably slow your network down a tad.
Contrary to a lot of popular advice, however, it’s not the only option. A lot of guides claim it is because mDNS reflection is the only tool they’re using. If you can put a controller with a leg on every segment, cleaner separation is available for a few trivial lines of Docker Compose YAML.
Finally, Sonos isn’t an exception to every networking rule. You can keep all the
units together on one VLAN, give the controller a leg there, and reflect
_sonos._tcp between Trusted and AV so phones can find them, and it works
itself perfectly well.
