
Set Up pfSense High Availability with CARP and pfsync
Before you run this
This guide builds a two-node pfSense high-availability (HA) pair: one primary firewall that carries traffic and one secondary that takes over automatically if the primary fails. CARP provides the shared virtual IP addresses your clients use as their gateway, pfsync copies the firewall state table between nodes so existing connections survive a failover, and XMLRPC config sync keeps the secondary's configuration in step with the primary.
There is no shell script here. The work is done in the pfSense webGUI as an administrator, plus console access. You do not need to drop to a root shell.
Read this before touching anything:
- This changes production forwarding and NAT. A wrong CARP VHID, a duplicate VIP, or a bad outbound NAT rule can black-hole traffic or cause an IP conflict on your LAN. Do it in a maintenance window.
- Keep a console open. Connect a monitor/keyboard or serial console to both boxes before you start. If the webGUI or a VIP misbehaves you must be able to reach the console menu (option 15, "Restore recent configuration") without depending on the network you are changing.
- Back up first. On each node go to Diagnostics > Backup & Restore and download the current
config.xml. pfSense also keeps automatic history under Diagnostics > Backup & Restore > Config History, which is your rollback path if a sync goes wrong. - The secondary gets overwritten. XMLRPC sync pushes the primary's config onto the secondary for every section you enable. Do this on a freshly installed or expendable secondary — anything already configured there for the synced sections will be replaced. This is not easily reversible on the secondary once it has synced, so start it clean.
- Test in a lab if you can. Two VMs on a virtual switch reproduce this whole setup. Prove the failover there before you build it on the boxes that carry your business.
Assumptions
I am writing for pfSense CE 2.7.x (the menu layout is the same on pfSense Plus 23.x/24.x). I assume:
- Two physical or virtual firewalls with identical interface assignments (same NIC on the same segment on both).
- Three interfaces in play: WAN, LAN, and a dedicated SYNC interface cabled directly between the two nodes (a crossover or a dedicated VLAN). Do not run pfsync over a shared production segment.
- You have spare IPs. Each node needs its own real address on each interface, plus one shared CARP VIP per interface.
The addressing plan
Every interface needs three addresses. Here is the example I will use — substitute your own ranges:
| Primary (real) | Secondary (real) | CARP VIP (shared) | |
|---|---|---|---|
| WAN | 203.0.113.2 | 203.0.113.3 | 203.0.113.1 |
| LAN | 192.168.1.2 | 192.168.1.3 | 192.168.1.1 |
| SYNC | 172.16.1.1 | 172.16.1.2 | — (no VIP) |
Clients point at the CARP VIP (192.168.1.1) as their gateway, never at a node's real IP.
Step 1 — Assign and address the SYNC interface
On both nodes:
- Interfaces > Assignments — add the dedicated NIC as a new interface (it will appear as OPT1; rename it to
SYNCunder its settings if you like). - Set it to Static IPv4: 172.16.1.1/24 on the primary, 172.16.1.2/24 on the secondary.
- Firewall > Rules > SYNC — add a rule allowing the traffic you need across this link. The safe, minimal approach is to allow only from the peer's SYNC address. If you prefer, allow all on SYNC since it is a private point-to-point link — pick one and be deliberate about it.
Step 2 — Configure state sync (pfsync) and config sync (XMLRPC)
Go to System > High Availability Sync.
State Synchronization Settings (pfsync) — set this on both nodes:
- Tick Synchronize States.
- Synchronize Interface:
SYNC. - pfsync Peer IP: the peer's SYNC address (172.16.1.2 on the primary, 172.16.1.1 on the secondary).
Configuration Synchronization Settings (XMLRPC Sync) — set this on the primary only. If you configure it on both, they fight each other.
- Synchronize Config to IP: the secondary's SYNC IP, 172.16.1.2.
- Remote System Username:
admin. - Remote System Password: the secondary's admin password.
- Tick the sections you want kept in step. At minimum sync Virtual IPs, Firewall Rules, NAT, and any service you rely on (DHCP, DNS Resolver/Forwarder). Leave Interfaces, hostname, and per-node settings unsynced — those are meant to differ.
Save. The primary will now push those config sections to the secondary whenever you save changes.
Step 3 — Create the CARP virtual IPs (on the primary only)
Do this on the primary. XMLRPC sync copies the VIPs to the secondary for you.
Go to Firewall > Virtual IPs > Add:
- Type:
CARP. - Interface: WAN (then repeat for LAN).
- Address(es): the shared VIP and its subnet mask — 203.0.113.1/24 for WAN, 192.168.1.1/24 for LAN. Match the real interface's mask.
- Virtual IP Password: a shared secret; it must be identical for every VIP in the group. Set the same value when you add the LAN VIP.
- VHID Group: a unique number per VIP (e.g. 1 for WAN, 2 for LAN). It must be unique on that broadcast domain — if another CARP device is present, avoid its VHIDs.
- Advertising Frequency: leave Base 1 / Skew 0 on the primary. When the VIP syncs, pfSense raises the skew on the secondary so it advertises as the backup automatically.
Save and Apply. Confirm the VIPs then appear on the secondary under Firewall > Virtual IPs.
Step 4 — Make NAT use the CARP VIP
With automatic outbound NAT, each node would translate to its own WAN address, which breaks on failover. Switch to shared translation:
- Firewall > NAT > Outbound.
- Set the mode to Hybrid Outbound NAT (keeps the automatic rules and lets you add your own) or Manual.
- Edit / add the outbound rule(s) for your LAN so the Translation Address is the WAN CARP VIP (203.0.113.1) rather than the WAN interface address.
Do the same for any port-forward or 1:1 NAT: point it at the CARP VIP, not a real interface IP. These sync to the secondary.
Step 5 — Point clients at the VIP
Set your LAN clients' gateway (and the DHCP server's advertised gateway/DNS) to the LAN CARP VIP, 192.168.1.1. If pfSense is your DHCP server, the HA DHCP settings sync via XMLRPC; confirm the failover peer IP under the DHCP service if you use it.
Verify it worked
- Status > CARP on each node. The primary should show every VIP as MASTER; the secondary should show the same VIPs as BACKUP. Any VIP stuck in INIT, or both nodes showing MASTER, means a mismatch — check that the VIP password, VHID uniqueness, and subnet masks match, and that CARP traffic is not blocked between them.
- Diagnostics > States — confirm the state count on the secondary tracks the primary; that proves pfsync is flowing.
- Make a small change on the primary (e.g. a firewall rule description) and confirm it appears on the secondary — that proves XMLRPC sync.
Test a real failover in your window: on the primary go to Status > CARP and click Enter Persistent CARP Maintenance Mode (this is the clean way to hand over — it demotes the primary without pulling cables). The secondary should go MASTER within a second or two and existing sessions should hold. Click Leave Persistent CARP Maintenance Mode to fail back.
How to undo it
- To take a node out of service temporarily, use CARP Maintenance Mode as above — reversible, no config change.
- To remove HA entirely: delete the CARP VIPs under Firewall > Virtual IPs, revert outbound NAT to Automatic under Firewall > NAT > Outbound, clear the pfsync and XMLRPC settings under System > High Availability Sync, and point clients back at a real interface IP.
- If a change breaks access, restore the
config.xmlyou saved, or use Diagnostics > Backup & Restore > Config History to roll back to the previous revision. From the physical console, menu option 15 restores a recent configuration when the GUI is unreachable.
For the authoritative reference — including the CARP, pfsync, and XMLRPC field details and current caveats — see the High Availability section of the official Netgate pfSense documentation.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
Related guides
Set Up Active-Passive HA on a Palo Alto Firewall Pair
This guide pairs two identical Palo Alto firewalls into an active-passive HA cluster : one firewall passes all traffic while the other sits in sync and takes over if the active one fails, reboots, or loses a monitored link. It does not load-balance — the passive box carries no production traffic until failover.
Configure ClusterXL High Availability on Check Point R81.20
This guide builds a ClusterXL High Availability (Active/Standby) pair from two Check Point gateways: two physical or virtual firewalls share one set of virtual IPs, and if the active member fails, the standby takes over the traffic and the connections. Its purpose is redundancy, not more throughput — HA mode passes traffic through one member at a time.
Configure High Availability on a SonicWall Pair
This guide pairs two identical SonicWall appliances into an Active/Standby High Availability cluster: the Primary handles all traffic, the Secondary sits synchronized and idle, and if the Primary fails the Secondary takes over the firewall's identity and keeps traffic flowing. That is the whole purpose — surviving a hardware failure without hand-rebuilding a box.
Set Up Active-Passive HA Between Two FortiGate Firewalls
This guide builds an FGCP active-passive cluster from two FortiGate firewalls: one unit passes all traffic while the second stays fully configured and idle, ready to take over within seconds if the primary fails. The two units share one configuration, one set of virtual MAC addresses, and appear on the network as a single firewall.




