OPNsenseLab
Redundant cable racks in a computing facility, evoking active-passive firewall failover
Advanced

OPNsense High Availability with CARP: Active-Passive Failover

Configure OPNsense HA using CARP virtual IPs and pfsync state synchronization for sub-second firewall failover — step-by-step for a two-node homelab or

By OPNsenseLab Editorial · · 8 min read

OPNsense supports active-passive HA using CARP (Common Address Redundancy Protocol) for virtual IPs and pfsync for connection state synchronization. When the primary node fails, the secondary takes over quickly with existing TCP sessions intact.

Before committing to this: HA is genuinely useful, but it roughly doubles your hardware, adds real configuration complexity, and introduces its own failure modes (split-brain, config drift, asymmetric routing). For most homelabs, a cold spare with a tested config backup recovers in minutes and is far simpler. Build CARP HA when you have services that genuinely cannot tolerate a multi-minute outage during a reboot or hardware failure — and when you can dedicate two near-identical boxes plus a dedicated sync link. If that’s you, read on; the payoff is sub-second, state-preserving failover.

A critical prerequisite people miss: CARP needs more than one usable IP on the WAN side. Your ISP must give you either a small static subnet (so primary, secondary, and the CARP VIP each have an address) or you must terminate the public IP on an upstream device and run CARP on a private transit network behind it. A single dynamic DHCP WAN address from a consumer ISP cannot do classic WAN CARP — plan the addressing before touching the GUI.

Architecture

ISP → [CARP VIP on WAN] → Primary OPNsense ←→ pfsync ←→ Secondary OPNsense

                           [CARP VIP on LAN] → Managed Switch → LAN

Both nodes have:

  • WAN interface (real IPs from ISP — you need two IPs or VLAN)
  • LAN interface
  • SYNC interface (dedicated link between nodes for pfsync + config sync)

Hardware

Two identical or compatible units. Common homelab pairs:

  • Two Protectli FW4C units
  • Two Topton N5105 mini-PCs with identical NIC cards

The sync interface needs at least a direct cable (modern NICs auto-MDIX, so a straight cable is fine) or a dedicated switch port/VLAN between the nodes. Keep pfsync off shared production links — a flooded LAN that delays state sync will cause failovers to drop existing connections, defeating the point.

NICs and ordering matter: both nodes should present interfaces with the same logical roles (WAN/LAN/SYNC). They don’t have to be identical hardware, but mismatched NIC counts or driver quirks make config sync fragile. Identical or near-identical units save hours of debugging.

Step 1: Network addressing plan

NodeWAN IPLAN IPSYNC IP
Primary203.0.113.2/30192.168.1.2/24172.16.0.1/30
Secondary203.0.113.3/30192.168.1.3/24172.16.0.2/30
CARP VIP WAN203.0.113.4
CARP VIP LAN192.168.1.1

Step 2: Configure the primary node

Create CARP virtual IPs

Interfaces → Virtual IPs → Add:

  • Type: CARP
  • Interface: WAN
  • IP address: 203.0.113.4/30
  • Virtual IP password: (shared secret, same on both nodes)
  • VHID group: 1
  • Advertising frequency: Base 1 / Skew 0 (primary always advertises faster)

Repeat for LAN CARP VIP.

Enable pfsync

Interfaces → Assignments → add the SYNC interface. Then:

System → High Availability → Settings:

  • Synchronize states:
  • Synchronize interface: SYNC
  • Synchronize peer IP: 172.16.0.2

Configure firewall rule for pfsync traffic

On the SYNC interface, allow pfsync protocol from 172.16.0.2.

Step 3: Configure the secondary node

Mirror the interface configuration with the secondary IPs. In HA settings:

  • VHID Skew: 100 (secondary advertises slower — primary wins by default)

Enable XMLSync to push config changes from primary to secondary: System → High Availability → Settings → Synchronize config to IP: 172.16.0.2

Step 4: Verify failover

# On a LAN client, ping the CARP LAN VIP continuously
ping 192.168.1.1

# Reboot the primary
# Observe: ping continues after ~1 second gap
# Secondary takes over the CARP VIP

Check System → High Availability → Status on both nodes to confirm MASTER/BACKUP state. A correct setup shows the primary as MASTER for every CARP VIP and the secondary as BACKUP for every one. Mixed states (some VIPs MASTER on each node) indicate a VHID, password, or subnet mismatch.

Also test the realistic failure modes, not just a clean reboot:

  • Pull the WAN cable on the primary — CARP should fail over if you’ve enabled interface monitoring; a primary that stays MASTER with a dead WAN is a classic misconfiguration.
  • Power-kill the primary (not graceful) — confirms the secondary promotes without a clean CARP demotion message.
  • Recover the primary — by default it preempts and reclaims MASTER. Decide whether you want that (fast return to normal) or prefer to disable preemption to avoid a second brief blip; set this consciously.

Common pitfalls

  • Both nodes showing MASTER (split-brain) — pfsync/CARP advertisements aren’t reaching the peer. Check the SYNC (and WAN/LAN) firewall rules allow the carp and pfsync protocols, and that VHID + virtual-IP password match exactly on both nodes.
  • Sessions not surviving failover — pfsync not enabled, the sync interface mismatched, or sync latency too high because pfsync shares a congested link. Give it a dedicated interface.
  • Config sync not working — verify the HA “Synchronize config to IP” points at the secondary’s SYNC IP, that the remote user/password (or API key) is correct, and that the secondary’s GUI is reachable on the sync network. Sync is one-directional: only ever edit the primary.
  • Asymmetric routing / states not matching — both nodes must use the CARP VIPs as the gateway/source for outbound and as the address clients target; mixing real and virtual IPs in NAT/outbound rules breaks state continuity on failover. Set Outbound NAT to translate to the CARP VIP, not the interface address.
  • DHCP/Unbound not failing over — enable DHCP failover (or static leases) and ensure the resolver is configured identically; clients pointed at a node-specific IP lose DNS when that node dies. Point clients at the CARP VIP.

Verification checklist

After a failover test, confirm: a continuous ping to the LAN CARP VIP recovered within ~1–2 seconds; an in-progress download/SSH session survived (proves pfsync state transfer); the Status page shows the new MASTER; and after the old primary returns, state is consistent again. Document the expected behavior so the next person doesn’t mistake normal preemption for a fault.

When not to deploy CARP HA

If your ISP only provides a single dynamic IP and you can’t run CARP on a private transit segment, classic WAN HA isn’t viable — don’t force it. If your uptime requirement is “a few minutes of downtime during a rare reboot is acceptable,” a tested config backup plus a cold/warm spare is dramatically simpler and has fewer ways to fail. HA adds value specifically when unattended, sub-second, state-preserving failover is a hard requirement and you can commit the second box and a dedicated sync link to get it.

Coming from pfSense? pfSenseLab has a CARP comparison between OPNsense and pfSense implementations.

Related

Comments