If you run two Pi-holes behind a keepalived VIP, your health check probably asks "does DNS answer?" Mine did. It catches a dead Pi-hole. It doesn't catch the one that's up, answering everything, and blocking nothing.
That's what happened to me recently. A sync tool (nebula-sync with RUN_GRAVITY=true) started twice in quick succession and ran two gravity rebuilds on my primary at once. The primary came out with no gravity table and every group mapping empty. DNS worked fine. Blocking was off for the whole house, and keepalived had no reason to move the VIP to the perfectly healthy backup.
So I wrote a second track script. It asks the node for doubleclick.net and fails only if it gets a real IP while blocking is switched on. That second part matters. Pausing Pi-hole sets dns.blocking.active to false, so a pause never moves the VIP. A broken database leaves it true.
Tested on v6 (FTL 6.7.1) with keepalived 2.2.7:
- forced failure on the primary: VIP moved in 28 s and came back 48 s after clearing, with no failed queries
- real mapping wipe on the backup: its priority dropped, VIP stayed put
- 5-minute pause of both nodes: nothing happened, which is the point
One trap if you write your own: don't use +norecurse for this. It's fine for a pi.hole liveness check, but an unblocked name is then answered only from cache, and the check flips between pass and fail without ever tripping.
Script, keepalived config and notes: Pi-hole + keepalived: fail over when Pi-hole stops blocking, not just when it stops answering · GitHub
Two related lessons, in case they save someone an evening of tinkering:
- I've turned
RUN_GRAVITYoff. Same fix as the "Nightly Failures of DB causing outages" thread posted previously. - nebula-sync also syncs on every container start, so a Docker upgrade or reboot is a sync. I've opened an issue asking for a way to turn that off: Every container start runs a full sync with RUN_GRAVITY, and gravity runs on the primary too. Two quick restarts wiped my primary. · Issue #271 · lovelaze/nebula-sync · GitHub
As-is, not a maintained project. Happy to answer questions here.