On this page
The seduction of config-sync
In the previous post I built a route-based Active/Active Palo Alto pair: both firewalls forwarding, eBGP to the N9K border pair, ECMP doing the load spreading, failover as a routing event. I ended by saying the complexity doesn't vanish, it moves. This is where it moves to.
PAN-OS config-sync is the feature that makes an A/A pair feel like one firewall: commit a security rule on one box and it appears on the other. The seduction is believing that means the two boxes are interchangeable. They aren't, and the place you learn that is NAT.
The Active/Passive world you came from
If you learned NAT on Active/Passive pairs, you never had to think about any of this, and it's worth seeing why before the A/A version makes sense. In A/P exactly one dataplane forwards. The SNAT pool has one owner, so dynamic-ip-and-port can hand out ports with no coordination. The DNAT addresses are answered by whichever box is active, and the passive box's interfaces stay quiet. When failover happens, HA2 has already copied the session table, translations included, so the new active box continues existing flows with the same public addresses and nobody outside notices. One box, one NAT identity, one owner for every translation: simple.
Active/Active breaks each of those assumptions differently, and this is the part the docs state but rarely explain. SNAT is stateful per-packet work against a shared resource (the port pool), and two live dataplanes cannot both allocate from one pool without inventing a distributed allocator, so PAN-OS refuses to let them: dynamic SNAT must be split per device, each box translating to its own address. Your network now has two egress identities at once, which is fine until an external allowlist only contains one of them. DNAT survives unchanged, because it's a stateless rewrite: any box can translate a published address inbound for a new session, so one rule bound to both devices serves the pair. And existing sessions keep the model coherent: whichever firewall owns a session decided its translation at setup, the synced session table records it, and HA3 delivers stray packets to the owner, so a flow never changes public identity mid-life even when its packets arrive at the wrong box. A/P hides NAT complexity behind a single active brain; A/A makes you write the split down explicitly. The commit errors below are PAN-OS insisting on exactly that.
Two commits that refused
The requirement sounds trivial: tenant traffic leaving through either firewall should source-NAT to that firewall's own egress address. Two obvious implementations, two commit failures:

The first failure is fundamental. Dynamic-ip-and-port NAT maintains a translation pool with live port allocations, and two independent dataplanes cannot coordinate one pool without becoming each other's bottleneck. PAN-OS doesn't try: DIPP source NAT simply refuses a device binding of both. The second failure is subtler: translate to interface-address and the commit resolver evaluates against the synchronized configuration view, where the peer's interface IP doesn't exist (interface config is never synced, by design). So it reports the interface has no IP, even though the box you're typing on clearly does.
The pattern: one rulebase, per-device rules

The design that commits clean and survives config-sync: write two explicit rules, identical on both firewalls, each bound to one device-id. Firewall A (device 0) applies fabric-snat and translates to its address; firewall B (device 1) applies fabric-snat-1 and translates to its own. The rulebase is byte-identical on both boxes, which is exactly what config-sync wants to enforce, and the device binding makes each box execute only its half:

In configuration terms the pattern is four lines of difference between two otherwise identical rules:

One nuance completes the picture: static destination NAT (the published-service kind) binds to both devices without complaint, because there's no shared pool to coordinate, just a stateless rewrite plus the session table that HA2 already synchronizes. The asymmetry is precise: DIPP source NAT is per-device work, static destination NAT is pair-wide truth.
How per-device NAT coexists with shared sessions
A fair objection: the previous post said HA2 synchronizes the session table and HA3 forwards asymmetric packets to the session owner. If firewall B can receive packets for a session firewall A owns, whose NAT applies? The answer is what makes the whole arrangement coherent: NAT is decided once, at session setup, by the session owner, and the chosen translation is recorded in the session itself. From then on every packet in that flow, whichever box it lands on, is forwarded to the owner over HA3 and leaves with the owner's translation. The device-bound rules only ever compete for the first packet of a new flow. That's also why the synced session table never contains a contradiction: a session references the rule that created it by name, and since the rulebase is identical on both boxes, the survivor knows exactly which rule to apply. The one honest exception is a source-NAT flow translated to the dead box's own egress address: that address goes down with the box, so those sessions break on the return path and re-establish through the survivor's address. Destination NAT and anything translated to a still-living address survives cleanly.
The full device-binding spectrum makes more sense once you see it through that lens. Binding 0 or 1 means "only this box initiates sessions with this rule": right for anything referencing per-device resources like a DIPP pool. Binding both means either box may set up sessions with the rule: right for static translations with no shared state, like the destination NAT that publishes services. And active-primary pins rule processing to whichever box currently holds the active-primary role: occasionally useful, but it reintroduces the role-following behavior this design deliberately avoids, so it stays unused here.
Verifying who did what
Operationally, the question you'll actually ask is "which box owns this flow and which NAT fired?" The session table answers both: show session all filter source 10.120.0.11 on either firewall finds the flow (the table is synced), and the session detail names the NAT rule and the session owner. If you see sessions owned by only one device for weeks, your ECMP hashing upstream deserves a look; a healthy pair shows ownership split somewhere near the middle, and checking it occasionally is cheap insurance that the switches' ECMP and the firewalls' setup distribution still agree.
Knowing what config-sync will never do
Config-sync moves policies, objects, zones, profiles, and NAT. It deliberately never touches interface IPs, virtual-router and BGP config, HA settings, device-id, or management. Read that list against the route-based design from the previous post and the shape snaps into focus: everything that makes the two boxes the same is synced; everything that makes them different routers is not. The eBGP sessions, the per-box addresses, the LACP bundles: each firewall owns its own, which is why the design works at all. There's a VR-sync option that syncs routing config too. For this design it stays off, permanently; syncing routing config between two boxes that must differ at layer 3 is how you generate identical router IDs and a very confusing afternoon.
Three operational notes from running it. First, the initial sync is a one-way overwrite from the box you commit on, so reconcile any drift before enabling it, not after. Second, keep per-firewall configuration exports as your rollback, because a synced pair can sync a mistake at line rate. Third, an API quirk: pushing device config immediately after a peer's commit can race the sync engine and return a spurious error; the change actually lands on retry.
The takeaway: config-sync isn't a promise that two firewalls are one; it's a contract about which parts should be. Write NAT per-device inside a shared rulebase, keep routing per-box, and the pair stays synchronized where it matters and independent where it must be. Next up: why this same pair still runs floating IPs on one side, and why that's correct.