Multi-tier networking diagram showing IoT, services, and management tiers

Multi-Tier Networking: Separating IoT, Services, and Management Networks

We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.

When I first started my homelab, everything lived on the same network. My Nextcloud instance, my smart bulbs, my Raspberry Pi—all chewing packets on 192.168.1.0/24. It felt simple until a malicious smart bulb (or a compromised one) on the network could theoretically reach my sensitive services. That's when I realized: flat networks don't scale securely. I needed isolation.

Today I'm running three distinct network tiers on my setup, and it's transformed how I think about homelab security. This isn't just theoretical—it's a practical architecture that fits on modest hardware and keeps untrusted devices away from your crown jewels.

Why Network Segmentation Matters for Homelabs

Your homelab is a target. Not from organized attackers (probably), but from:

A flat network means all of these are one pivot away from accessing your Vaultwarden, your Nextcloud, your backups. Multi-tier segmentation breaks that path. If a smart bulb gets compromised, it can't reach your management network. If a guest device is infected, it can't touch your services tier.

I think of it as defense in depth: layers that force an attacker to breach multiple boundaries, not just one.

The Three-Tier Model I Use

Tier 1: IoT Network (192.168.10.0/24)
Smart bulbs, cameras, thermostats, any device you don't fully control. Low trust. Can reach the internet and maybe the management network for basic queries, but cannot initiate connections to services or management tiers.

Tier 2: Services Network (192.168.20.0/24)
Your Nextcloud, Jellyfin, Immich, Vaultwarden—the apps you're self-hosting. Can reach the IoT tier in *limited* ways (like a camera sending video to Frigate). Cannot reach the management tier.

Tier 3: Management Network (192.168.30.0/24)
Your NAS, your backup server, your SSH gateway, your Kubernetes control plane—the infrastructure you administer. Highest trust. Can reach all other tiers, but other tiers cannot initiate connections here.

The key rule: higher tiers can reach lower tiers (with restrictions), but lower tiers never originate connections to higher tiers. It's not about blocking all traffic—it's about direction and consent.

Setting Up VLANs on Your Switch

If you're running physical hardware, VLANs are your foundation. I'm using a TP-Link ER605 ($60–80) as my router with a managed switch. Here's my physical setup:

On the TP-Link ER605, I've created three VLAN interfaces:

# SSH into your ER605 (or use web UI)
# VLAN 10 (IoT)
ip link add link eth0 name vlan10 type vlan id 10
ip addr add 192.168.10.1/24 dev vlan10
ip link set vlan10 up

# VLAN 20 (Services)
ip link add link eth0 name vlan20 type vlan id 20
ip addr add 192.168.20.1/24 dev vlan20
ip link set vlan20 up

# VLAN 30 (Management)
ip link add link eth0 name vlan30 type vlan id 30
ip addr add 192.168.30.1/24 dev vlan30
ip link set vlan30 up

# Verify
ip link show | grep vlan
ip addr show | grep 192.168
Tip: Most managed switches (netgear, TP-Link, Ubiquiti) have web UIs for VLAN config. You don't need CLI. I prefer CLI because it's faster and reproducible, but web UI is fine if you're new to this.

Firewall Rules: The Enforcement Layer

VLANs isolate at layer 2, but you need layer 3 (firewall) rules to actually enforce the trust model. Here's my firewall configuration on the ER605 (using iptables pseudocode, adapt to your platform):

# Default policies
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Rule 1: Services → IoT (allowed for specific protocols)
# Example: Frigate camera service needs RTSP from IoT cameras
iptables -A FORWARD -i vlan20 -o vlan10 -p tcp --dport 554 -j ACCEPT
iptables -A FORWARD -i vlan20 -o vlan10 -p tcp --dport 8554 -j ACCEPT

# Rule 2: IoT → Services (drop by default)
iptables -A FORWARD -i vlan10 -o vlan20 -j DROP

# Rule 3: Management → Everything (trusted)
iptables -A FORWARD -i vlan30 -o vlan10 -j ACCEPT
iptables -A FORWARD -i vlan30 -o vlan20 -j ACCEPT

# Rule 4: Everything → Management (drop by default)
iptables -A FORWARD -i vlan10 -o vlan30 -j DROP
iptables -A FORWARD -i vlan20 -o vlan30 -j DROP

# Rule 5: All tiers → WAN (internet, for updates and outbound)
iptables -A FORWARD -o eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT

# Rule 6: Loopback (internal tier communication)
iptables -A FORWARD -i vlan10 -o vlan10 -j ACCEPT
iptables -A FORWARD -i vlan20 -o vlan20 -j ACCEPT
iptables -A FORWARD -i vlan30 -o vlan30 -j ACCEPT

This might look scary, but the logic is simple:

I test my rules with ping and nmap from each tier, confirming what should and shouldn't work.

Extending Segmentation with Docker Networks

For containers, physical VLANs aren't enough—you also need Docker network isolation. When my Nextcloud and Jellyfin containers are running on the same Docker daemon, they can reach each other by default. I don't want that.

Here's my Docker Compose approach for services tier:

version: '3.8'

networks:
  jellyfin_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
  nextcloud_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.21.0.0/16
  ingress_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.22.0.0/16

services:
  # Jellyfin (isolated)
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    networks:
      - jellyfin_net
      - ingress_net
    volumes:
      - /mnt/media:/media:ro
      - jellyfin_config:/config
    environment:
      - JELLYFIN_PublishedServerUrl=https://jellyfin.yourdomain.com
    restart: unless-stopped

  # Nextcloud (isolated)
  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    networks:
      - nextcloud_net
      - ingress_net
    volumes:
      - nextcloud_data:/var/www/html
      - /mnt/nfs/nextcloud:/mnt/external
    environment:
      - SQLITE_DATABASE=nextcloud
      - NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.yourdomain.com
    restart: unless-stopped

  # Caddy reverse proxy (sits on ingress_net only)
  caddy:
    image: caddy:latest
    container_name: caddy
    networks:
      - ingress_net
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped

volumes:
  jellyfin_config:
  nextcloud_data:
  caddy_data:
  caddy_config:

Notice the key detail: Jellyfin and Nextcloud are on *separate* networks and cannot reach each other directly. They both connect to ingress_net (where Caddy lives), so Caddy can route traffic to them, but they're isolated from each other. If Nextcloud is compromised, the attacker can't pivot to Jellyfin's database.

Verify isolation:

# From the Nextcloud container, try to reach Jellyfin by hostname
docker exec nextcloud ping jellyfin
# Output: ping: jellyfin: Name or service not known

# Try to reach by IP (even worse—it won't work either)
docker exec nextcloud ping 172.20.0.2
# Output: connect: Network is unreachable
Watch out: Docker's default `bridge` network has DNS resolution for container names. This is convenient but a security leak if you need true isolation. My approach above uses *separate* bridge networks, so containers can't even resolve each other by name. The ingress network (Caddy) is the only path between tiers.

Management Access: The Admin Tier

Your management network needs special handling. This is where you SSH to servers, manage your NAS, run Kubernetes, or access Portainer. You want it highly isolated but still functional.

I keep my management devices on a separate physical interface or VLAN and access them via a jump host. Here's the pattern:

From my laptop (on home WiFi, which is VLAN 10—IoT tier):
→ SSH to a public VPS (~$40/year on RackNerd or similar)
→ Tailscale tunnel from VPS to my management gateway
→ SSH from management gateway to internal infrastructure

This way, my untrusted devices never get a direct route to management. Even if my laptop is compromised, the attacker has to breach the VPS and then the Tailscale tunnel.

If you don't want a VPS, use a WireGuard gateway inside your management tier itself—same principle, local instead of cloud.

DNS and DHCP Across Tiers

Each VLAN needs DHCP. I run dnsmasq on the router with per-VLAN configs:

# /etc/dnsmasq.d/vlans.conf

# VLAN 10 (IoT)
interface=vlan10
dhcp-range=vlan10,192.168.10.100,192.168.10.200,24h
dhcp-option=vlan10,3,192.168.10.1
dhcp-option=vlan10,6,192.168.10.1
server=/iot.local/192.168.10.1

# VLAN 20 (Services)
interface=vlan20
dhcp-range=vlan20,192.168.20.100,192.168.20.200,24h
dhcp-option=vlan20,3,192.168.20.1
dhcp-option=vlan20,6,192.168.20.1
server=/services.local/192.168.20.1

# VLAN 30 (Management)
interface=vlan30
dhcp-range=vlan30,192.168.30.100,192.168.30.200,24h
dhcp-option=vlan30,3,192.168.30.1
dhcp-option=vlan30,6,192.168.30.1
server=/mgmt