Skip to main content
The homelab network is described in HCL. Every VLAN, WLAN, port profile, WAN uplink, and the VPN server is an OpenTofu resource — adopted from the live controller, not built from scratch.
tofu-unifi puts the UniFi controller’s data plane behind the same Terrakube provisioning workflow as the rest of the homelab. The ubiquiti-community/unifi provider drives the controller API; typed HCL inputs and locals resolve subnets.

What it does

One generic module per resource type covers the controller’s data plane:
  • Infrastructure VLANs / networks, WLANs, and per-role port profiles
  • MAC-pinned fixed-IP reservations
  • Firewall groups (the port/address sets); firewall rules (42 rules total, including a generic established/related stateful-return rule) are written but deferred — see provider notes
  • WAN uplinks (primary, secondary, LTE failover), the WireGuard VPN server, RADIUS, and the controller’s dynamic-DNS record
  • Subnet values are non-secret desired state shared by both repos. Passphrases, VPN keys, and shared secrets come ephemerally from native OpenBao paths.

How it fits

Adoption model

The controller came first; the code adopts it. Existing objects are pulled in with import blocks (by controller ID, by MAC for clients, by name for WANs) and then field-aligned so the committed config matches reality — a clean adoption plans to zero change, not a re-create. Two safety rails are non-negotiable: a full controller snapshot is committed to a companion read-side repo as the rollback reference before every apply, and the plan is reviewed for any destroy/replace before it runs. New firewall rules ship disabled and are flipped on one at a time in later changes.

Why per-service VLANs

The homelab uses VLAN-as-service-tier: a guest’s VLAN encodes what kind of workload it is, and therefore which policies apply to it. That keeps the inventory comprehensible at a glance and lets firewall policy attach to a tier rather than to individual hosts.

Two enforcement layers, one default-deny model

Firewall policy for inter-VLAN traffic is enforced at two layers, both built to the same default-deny model: deny everything, then explicitly allow the service flows that need to cross a VLAN boundary (plus DNS, NTP, and management baselines). The guest-level layer — each Proxmox VM/LXC’s own firewall, provisioned by tofu-proxmox — is live today: every guest’s default input and output policy is deny, with per-service allow rules opened only for the ports that guest actually needs. The network-level layer — the 42 deferred UniFi inter-VLAN and local rules described above — is written to the same model but not yet enforced; it activates once the provider/controller rule-index gap closes.

Provider notes (UniFi Network 9)

Provider notes (UniFi Network 9)

The ubiquiti-community/unifi provider is current and capable, but it lags UniFi Network 9 in a few spots worth knowing before you adopt a controller. These are provider/controller version gaps, not configuration mistakes:
  • Legacy firewall rules vs the new index range. Network 9 renumbered gateway firewall-rule indices; the provider still validates the old range. New rules can’t be created in either band until the provider catches up, so rule config is written but parked (disabled) for now. Firewall groups are unaffected.
  • No zone-based firewall resources yet. Network 9’s zone-based firewall has no provider resource; that policy stays UI-managed.
  • No IPS / threat-management / suppression resource. Source-direction IDS/IPS suppression — e.g. exempting a known-opaque VPN tunnel’s source host from inspection, see VPN-locked egress reliability — has no OpenTofu resource either; it stays a live-console setting.
  • Failover-only WAN. A failover-only uplink stores a sentinel priority the provider’s validation rejects — omit the priority and let the load-balance type imply it.
  • No static-WAN-IP attribute. A static WAN’s address is read-only from the provider’s side; it stays controller-managed and imports cleanly.
  • Benign per-plan churn. The provider re-proposes a handful of computed fields on every plan (WLAN, port-profile, RADIUS defaults). These aren’t real changes — they apply idempotently and don’t mutate the controller.
The rule of thumb when the provider rejects a controller-correct value: pin what you can, defer what you can’t, and document it — don’t fight the provider.

Authentication

UniFi services use API-key authentication. Credential management details are documented in internal documentation.

Network topology

Solid green edges are physical / network. WireGuard tunnels traverse the Internet → UniFi edge. The UniFi gateway is the centre of the LAN; Proxmox, personal devices, and the bare-metal LLM box all hang off it.

tofu-proxmox

The provisioner that lands VMs/LXCs on these VLANs.

Self-hosted Netflix

Self-hosted Netflix — its own VLAN.

LXC vs Docker

Why most workloads on these VLANs are LXC, not Docker.

VPN-locked egress reliability

Sticky VPN-endpoint failover, and an IDS/IPS false-positive lesson.

Source on GitHub

Provider config, networks, port profiles, firewall rules.