← Field notes

pfSense REST API v2 on Netgate | install, tradeoffs, and what it's good for

August 5, 2026 · 3 min read ·
networkinghomelabpfsenseautomationnetgate

pfSense has a GUI for everything. That’s fine until you need the same change on three services, want a read-only check before a cutover, or need DNS/DHCP/syslog to stay in sync with the rest of your automation. Clicking through Services → DHCP Server every time a hostname or upstream moves is how config drift starts.

The community REST API v2 package (maintained by pfrest, not Netgate) closes that gap. I run it on a Netgate 2100 with pfSense Plus. It is not in System → Package Manager on Plus builds, so install is a manual pkg-static pull from GitHub releases matched to your /etc/version.

Install (two minutes on the firewall shell)

SSH in (console option 8 on Netgate), confirm version, install the matching .pkg:

cat /etc/version
# Example for 26.03.1:
pkg-static -C /dev/null add -f \
  https://github.com/pfrest/pfSense-pkg-RESTAPI/releases/download/v2.8.2/pfSense-26.03.1-pkg-RESTAPI.pkg

Then in the GUI:

  1. System → REST API → Settings: enable Key authentication (and/or local database if you prefer basic auth).
  2. System → REST API → Access Lists: allow only your automation host or lab subnet, not 0.0.0.0/0.
  3. System → REST API → Keys: create a least-privilege key (DNS Resolver + DHCP Server write for my use case; avoid WebCfg - All unless you mean it).
  4. System → REST API → Documentation: Swagger UI for endpoint discovery.

Smoke test from a trusted host:

export PFSENSE_URL="https://<firewall-ip>:4443"   # LAN gateway, or your nginx ingress hostname
curl -ksS -H "X-API-Key: YOUR_KEY" \
  "${PFSENSE_URL}/api/v2/status/system" | jq '.status, .data.hostname'

How it helps (what I actually use it for)

Once the API is up, the firewall becomes scriptable the same way the rest of the homelab is:

  • Read-before-write gates. A preflight script hits DHCP, resolver, and DNS settings over GET and exits NO-GO if the network isn’t in the shape the plan assumes. That caught a real outage before a DNS cutover wrote anything. See the Netgate DNS Cutover Toolkit write-up.
  • Idempotent policy apply. PATCH with --dry-run first, then --confirm when the diff is what I expect. DHCP DNS server, Unbound host overrides, system DNS: one toolkit instead of six GUI tabs.
  • DNS sync from canonical source. Push lab host overrides to Unbound from a single target list in git, so the firewall stays authoritative without hand-editing overrides in the GUI.
  • Remote syslog to Splunk. PATCH /api/v2/status/logs/settings to point filterlog at the homelab indexer. No clicking through Status → System Logs → Settings during an incident.
  • Swagger for exploration. Faster than grepping the GUI when you’re trying to learn what v2 exposes on your build.

The pattern is the same as any production change control: discover state, diff against intent, apply with an explicit flag, verify on a real client.

Pros

  • Real automation surface. v2 covers DHCP, Unbound, system DNS, syslog, and more. Enough to treat pfSense like infrastructure, not a one-off appliance.
  • Key-based auth with access lists. Scope who can call the API and from which subnets. Better than sharing the admin password with a cron job.
  • Swagger docs in the GUI. Self-documenting for the build you actually installed.
  • Fits bash/Ansible/curl workflows. No vendor SDK required. jq, curl, and a shared helper file got me most of the way.
  • Free. Community package, no license tier unlock.

Cons (read these before you install)

  • Not official Netgate packaging on Plus. You own version matching, upgrades, and breakage if pfSense revs before pfrest ships a .pkg. Pin the release URL to your exact version string.
  • Another attack surface. An API key with write access to DHCP or DNS is a fast path to locking yourself out or handing clients the wrong resolver. Least privilege, IP allow lists, key rotation.
  • Community support model. Bugs and API quirks are GitHub issues, not Netgate TAC. I hit one myself: sending Content-Type: application/json on bodyless GETs makes pfSense look for id in a JSON body and return MODEL_REQUIRES_ID even when ?id=lan is in the query string. Easy fix once you know; maddening until you do.
  • Manual install on Plus. No one-click Package Manager entry. Fine for a homelab you operate; harder to sell on a fleet you don’t touch.
  • Apply semantics bite. Endpoints with ?apply=true push config live. Pair every write path with read-only preflight or dry-run. I don’t run destructive applies without a snapshot or rollback plan.
  • Not a replacement for the GUI. WAN policy, complex NAT, and one-off troubleshooting still belong in the UI with human eyes.

When I’d skip it

If the firewall is set-and-forget and you touch it twice a year, the GUI is enough. If you can’t restrict API keys and access lists, don’t install it. If you need vendor-backed API support on a production edge with SLAs, evaluate what Netgate officially documents for your tier before betting the edge on a community package.

For a homelab or small MSP lab where you are the change control board and you want DNS/DHCP/syslog to move with the rest of your scripts, the tradeoff is worth it.

← Older
Making pfSense the front door to my LAN over Tailscale
Newer →
When Tailscale breaks your LAN host's gateway