Terraforming my home network with pfSense

I run pfSense as my home router. The configuration is in Terraform. The Terraform state is in a private Git repo. When I change the network, I open a PR, review the diff, and merge. The change applies when the merge happens.

This sounds like overkill for a home network. It is. It’s also the only way I’ve found to keep a home network from drifting into chaos over five years.

Why Terraform

A home network config drifts. You add a firewall rule for a one-off test and forget to remove it. You add a port forward for a project and don’t document it. You change the DHCP range and the static mappings stop making sense. Three years in, you have 200 firewall rules and no idea which ones you need.

With Terraform, the config is a file. The file is in git. The git history shows every change. The PR review is the documentation. The diff is the changelog.

The win is not automation. The win is that the config is reviewable.

The setup

  • pfSense VM on the Proxmox cluster (one core, 512 MB RAM, two virtual NICs)
  • One NIC is the WAN (connected to the cable modem)
  • One NIC is the LAN (connected to the home switch)
  • Terraform runs from my laptop, talks to pfSense over the LAN, using the pfSense Terraform provider
  • The state file is in a private Git repo, encrypted at rest
  • The change pipeline is: edit .tf files locally, terraform plan, terraform apply if the plan looks right

The Terraform provider for pfSense is dlaszlo/pfsense. It’s community-maintained, covers most of the common resources (firewall rules, aliases, NAT, DHCP static maps, DNS resolver overrides, VLAN interfaces), and is good enough for a home network.

What’s in the config

  • Firewall aliases for the static devices (the NAS, the printer, the homelab nodes, my laptop, the kids’ devices)
  • Firewall rules per alias (so “the NAS” is a real concept in the rules, not an IP address)
  • VLAN definitions (guest, IoT, lab, management)
  • Inter-VLAN rules (deny all by default, allow specific ports where needed)
  • NAT rules (only the ones that are actually needed, with comments explaining why)
  • DNS resolver overrides (the pihole at pihole-01.lan is the upstream, with Cloudflare as the fallback)
  • DHCP static maps for the things that need a stable IP

The whole config is about 600 lines of HCL. I can read it end to end. I can answer “why is this rule here” by reading the commit that added it.

The change loop

  1. Notice something needs to change (a device needs a static IP, a port forward is needed for a project, a rule is wrong)
  2. Edit the Terraform file
  3. Run terraform plan and read the output
  4. If the plan looks right, terraform apply
  5. Commit the change to the Git repo with a message explaining why
  6. The diff in the commit is the documentation

The loop takes 5-10 minutes for a small change and 30-60 minutes for a new VLAN. Compare to: opening the pfSense web UI, clicking through 5 screens, fat-fingering an IP, and not remembering what you did six months later.

What doesn’t work

  • terraform import for existing firewall rules. The provider wants you to declare everything in HCL, then apply to push the config. If you have an existing pfSense with 200 rules, importing them all is painful. I started fresh.
  • Anything that requires a pfSense package the provider doesn’t cover. I run pfBlockerNG for the DNS blocklists, and the Terraform provider doesn’t touch that. I configure pfBlockerNG by hand in the web UI. It doesn’t change often.
  • Anything in the pfSense web UI that’s a one-off (diagnostics, package manager). Those are still done by hand.

The thing I learned the hard way

The first time I ran terraform apply, it wiped out my existing firewall rules. The provider’s “create or update” logic is not always safe. I had a backup of the previous config. The restore took 20 minutes. The next change I made was to a fresh config in a fresh repo.

The lesson: Terraform is good for declaring what you want. Terraform is bad for adopting an existing config you didn’t declare. Start from scratch or budget a weekend for the migration.

The cost

About 8 hours to set up the first time, including learning the provider. About 1 hour per change since then. The 8 hours were spent in the first month. The per-change time is what I’m saving now, three years in.

If you have a home network you plan to keep for more than a year, this is worth it. If you move every year and reset everything, skip it.


← back to all posts