From Flat LAN to Segmented Homelab
planning and building a VLAN-segmented homelab network — tiers, a build order that avoids any outage, a real VPN kill switch, and the mistakes worth skipping.
in building my homelab — part 1 i mentioned i’d probably start segmenting the network with VLANs “once things get harder to manage.”
things got harder to manage.
this is the walkthrough i wish i’d had — planning, the build order that costs zero downtime, a VPN-isolated segment with an actual kill switch, and the mistakes that ate the most time.
why segment at all
a flat network means every device trusts every other device by default. a compromised smart plug can talk to your NAS. a guest on your wifi can see your admin panels. it works fine right up until it doesn’t.
the real trigger for me wasn’t a security incident — it was realizing i wanted to add a dedicated automation stack that needed to always route through a VPN, and bolting that onto a flat network with a per-host firewall alias felt like exactly the kind of thing that fails quietly six months later.
structural isolation beats “remember to keep this list updated,” every time.
homelab tiers, roughly
| tier | budget | what it looks like |
|---|---|---|
| starter | $0–50 | stock router, maybe a pi-hole |
| basic self-hosting | $100–400 | one mini-PC on Proxmox, still one flat network |
| segmented (this guide) | $400–900 | managed switch, dedicated router OS, VLANs |
| advanced | $900+ | clustered nodes, dedicated VPN-routed workloads, real NAS |
you don’t need “advanced” hardware to build a “segmented” network — the router does all the VLAN logic. more compute nodes is a completely separate axis.
planning the VLAN scheme
a generic starting table:
| VLAN | purpose | who lives here |
|---|---|---|
| Management | admin plane | router, switch, AP, hypervisor GUIs |
| Clients | personal devices | your phone/laptop — not the same as Management |
| Infrastructure | shared services | DNS, dashboards, password manager |
| Media/Apps | direct-internet services | streaming, general self-hosted apps |
| Isolated-VPN | forced-VPN egress | anything that must never leak your real IP |
| IoT | smart-home devices | assume compromised by default |
| Guest | visitor wifi | internet-only, no internal access |
| Lab | experiments | anything unconfirmed or throwaway |
| Storage | NAS/file services | SMB, NFS, iSCSI |
trim what you don’t need. a single-person homelab probably skips Guest. a household with kids’ devices might want it day one.
do you actually need a managed switch?
yes, if you want more than one physical device carrying tagged VLAN traffic. an unmanaged switch forwards everything as one broadcast domain and strips 802.1Q tags — your router-side VLAN config sits there doing nothing until it’s replaced.
port budget, before you buy: 1 router uplink + 1 trunk per multi-VLAN host + 1 access port per single-purpose device + real headroom for hardware you haven’t bought yet. undercounting this is the most common “should’ve gotten the bigger switch” regret.
the build order that costs nothing
this is the part that surprised me — you can build almost the entire thing before buying anything.
- create VLAN interfaces on the router — config objects only, inert until wired to a trunk
- assign + enable each one, give it a static
.1gateway address - build a DHCP subnet per VLAN — pool range, router, DNS
- add static reservations for anything you’ll want to find by IP
- hold off on repointing system-wide DNS — more on that below
- build firewall rules — default-deny, allow only what’s listed
- then buy and rack the switch, migrate devices one at a time
steps 1–6 are safe because an unmanaged switch strips 802.1Q tags regardless of what your router thinks it’s doing. i built the entire VLAN interface/DHCP/firewall config over a few sessions with zero risk to the network i was actively using, weeks before the switch showed up.
step 5 is the one exception — it’s the one setting that’s global rather than scoped to an unwired VLAN. flip it too early and you break live DNS resolution immediately, since the new addresses aren’t reachable yet.
step 1, field by field
one VLAN interface per row of your table, but the fields worth double-checking rather than clicking through on autopilot:
| field | what to set it to | why |
|---|---|---|
| device name | leave auto-generated if your router offers that | most modern router OSes will name it for you, no reason to hand-type one |
| parent interface | your actual physical LAN NIC | verify this against the live config, not an old note. i had the wrong NIC name written down for weeks, copied from an early draft instead of checked against the box — didn’t bite me since nothing was wired up yet, but it would have if I’d built firewall rules against it blind |
| VLAN tag | your chosen number for this VLAN | one interface creation per tag |
| priority | best-effort / default | nothing in a homelab needs 802.1p traffic prioritization — if you’re inheriting a config, check for a stray “max priority” setting nobody meant to set, and don’t copy it into new VLANs |
| description | matches your VLAN table’s name | future-you doing firewall rules will thank present-you |
step 2, field by field
once assigned, each interface gets its own IP config tab:
| field | what to set it to | why |
|---|---|---|
| enable interface | checked | obviously required |
| IPv4 configuration type | static | each VLAN interface is the gateway for that subnet, not a DHCP client of something else |
| IPv4 address | your chosen .1 (or equivalent) for this subnet |
|
| subnet size | /24 (or your chosen size) |
|
| upstream/gateway rules | disabled | these are internal VLAN interfaces, not WAN — there’s no upstream gateway to select here |
| block private networks | unchecked | this interface’s own traffic is a private range — checking this blocks the VLAN’s own subnet from itself |
| block bogon networks | unchecked | same logic — bogon filtering is a WAN-facing protection, not something to apply to an internal interface |
| MTU | leave blank | inherits the standard default, no jumbo frames in play here |
the VPN-isolated VLAN pattern
if you have any workload that should always exit through a VPN, don’t rely on a per-host alias or an in-container VPN client alone. give it its own VLAN and enforce the routing structurally.
two firewall rules, in order:
| # | action | gateway | purpose |
|---|---|---|---|
| 1 | pass | VPN tunnel | force all matching traffic through the tunnel |
| 2 | block | (none) | catch anything rule 1 couldn’t route, and drop it |
rule order is the entire mechanism — most firewalls evaluate top-down, first match wins. rule 2 only fires when rule 1 failed to route through the tunnel, which is exactly the failure mode you’re guarding against: silently falling back to your real IP instead of just not working.
a plain in-container VPN client works too, but ties the VPN config to that one container’s definition and usually has no real kill switch — if the client process dies, traffic can fall back to the host’s normal route depending on how it’s wired. a VLAN-level kill switch protects everything on that VLAN, regardless of what’s running there.
the middle ground, if you don’t have a managed switch port to spare for a dedicated VLAN yet: a single gateway container that runs the VPN client and a fail-closed iptables ruleset, with the other containers pointing their default route at it. same “force through the tunnel, drop everything else” logic, one layer down. that’s what i actually run today — wiring up a vpn-gated *arr stack is the full walkthrough. the VLAN version below is still the target; the gateway container is what covers the gap until the hardware’s there.
worked example, field by field — this is the highest-stakes rule pair in the whole build, since a mistake here leaks real traffic instead of just breaking something loudly:
rule 1 — force VPN egress:
| field | value | why |
|---|---|---|
| action | pass | allow the traffic, but… |
| interface | your VPN-isolated VLAN | this VLAN only |
| direction | in | standard for a LAN-side rule |
| protocol | any | covers every traffic type this VLAN’s workloads generate |
| source | this VLAN’s network | anything on this VLAN |
| destination | any | |
| gateway | your VPN tunnel gateway | this is what forces egress through the tunnel instead of the default route — the entire point of the rule |
rule 2 — kill switch, placed directly below rule 1:
| field | value | why |
|---|---|---|
| action | block | |
| interface | same VLAN | |
| direction | in | |
| protocol | any | |
| source | this VLAN’s network | |
| destination | any | |
| gateway | leave default — no override | with no gateway override, this rule only matches traffic rule 1 failed to route — i.e. exactly when the tunnel is down. rule 1 already caught everything else |
every other VLAN’s rules follow the same field pattern (action/interface/direction/protocol/source/destination) — just allow-listed ports and destinations instead of a gateway override, and no kill switch rule, since they’re not forcing traffic through a tunnel in the first place.
keeping rules from hardcoding IPs everywhere
before writing rules for every VLAN, build a small set of aliases — named groups of hosts/ports/networks the rules reference instead of typing raw addresses into every single rule. worth having at minimum:
- a private-ranges alias, for anything that needs to match “any internal network”
- a hosts alias per shared service devices need to reach across VLANs (your DNS resolver, your management hosts, a shared file share)
- a ports alias for common groups (web + DNS + NTP, say)
the payoff shows up later: when a shared service’s IP changes, you edit the alias once instead of hunting down every rule that referenced it directly.
the exceptions that cross VLANs
default-deny means nothing crosses a VLAN boundary unless a rule explicitly says so. the actual exception list ends up short:
- any VLAN → your DNS resolver alias, on the DNS port
- your admin VLAN → hypervisor/router management ports, from wherever you actually manage things
- trusted personal devices → the same management ports, if you’d rather not put your own daily-driver devices directly on the admin VLAN
- specific service VLANs → whatever shared service they legitimately need (a media server’s ports, a shared storage alias) — scoped to the alias/pinned IP from earlier, not the whole VLAN
everything else stays denied. if something new needs cross-VLAN access later, that’s a deliberate rule you add, not a default you’re fighting against.
cutover
everything above can be built with zero live impact. cutover is the one phase that actually touches your running network — do it deliberately, one device at a time.
- wire the router’s uplink into a trunk port on the new switch
- move multi-VLAN hosts (hypervisors) to trunk ports, tag their VMs onto the right VLANs
- move single-purpose devices to access ports on their assigned VLAN
- for any device with a manually configured static IP, switch it to DHCP client so it actually picks up its reservation — easy to forget, and the reservation does nothing until you do this
- now repoint system-wide DNS, since the new addresses are finally reachable
- retire the old unmanaged switch
then verify: each VLAN reaches exactly what it should, DNS resolves everywhere, export a config backup once it’s stable.
wireless — the gap i’m accepting for now
consumer mesh wifi is almost universally VLAN-incapable — no per-SSID tagging. that’s what i’m running, so wireless segmentation waits until i replace it with VLAN-aware APs. deliberate decision, not an oversight — worth writing down as one instead of quietly forgetting about it.
when that AP shows up, the plan is a Trusted SSID → Clients VLAN, an IoT SSID → IoT VLAN, a Guest SSID → Guest VLAN. the AP’s uplink becomes a trunk port, same trunking concept as any multi-VLAN host.
ipv6 — worth deciding now, rolling out later
two things about IPv6 are cheap to lock in while you’re already designing the scheme, and expensive to retrofit: pick a deterministic mapping from VLAN tag to a v6 subnet nibble (so VLAN 20 → ...:20::/64, readable at a glance), and generate one random ULA /48 for the whole homelab up front. point internal DNS and firewall rules at the ULA, never at whatever global prefix your ISP hands you — that prefix can change, the ULA won’t.
the actual rollout — WAN prefix delegation, per-VLAN address assignment, router advertisements, AAAA records, allowing ICMPv6 through the firewall — is a separate pass, and fine to do well after the v4 cutover is stable. just don’t let “later” mean you designed the v4 scheme in a way that makes the v6 nibble mapping awkward.
lessons learned
trust the live config, not the old doc. i had the wrong physical NIC name written down for weeks, copied from an early note instead of verified against the actual box. didn’t cause a live problem — nothing was wired up yet — but it would have if i’d built firewall rules against it blind.
the menu moves between versions. a documented path like “Interfaces → Other Types → VLAN” had actually moved to “Interfaces → Devices” on the version i was running. verify the live menu before following any written path literally.
the “everything just broke” moment that wasn’t. mid-session, DNS started intermittently timing out and recovering on its own. turned out every config change i applied was triggering a brief resolver reload — a few seconds of timeout, entirely expected, not a real break. know this ahead of time so you don’t burn twenty minutes debugging a non-issue.
two boxes i assumed were clustered weren’t. i’d documented two hypervisor hosts as a cluster, based on an old assumption instead of a checked fact. the actual cluster view showed only one node the whole time — they were two independent standalone installs. check the running state, not your memory, especially for anything you didn’t personally set up recently.
the pattern across all four: verify against the live system. infrastructure drifts, docs go stale, the box in front of you is the source of truth.
part 3 will probably be wireless, once a real AP shows up. or storage, if the NAS happens first. we’ll see which one gets harder to put off.