entry-19
The HA DNS pair finally syncs: from Gravity Sync to Nebula Sync
Nebula Sync replaced an unverified replication layer on the Pi-hole v6 pair. A live session on 2026-10-05 deployed it, proved the alert path with a real failure, passed a controlled failover, and rotated a VRRP password that had been exposed in screenshots.
revisions rev. 1 first set 2026-10-23 last revised 2026-10-23 3 min read
series / HA DNS part 9 of 9 all parts → ← prev
The lab’s DNS architecture has two Pi-hole and Unbound VMs behind a Keepalived virtual IP. The original physical-Pi build used Gravity Sync for configuration replication, with a captured sync comparison to prove it worked. The VM migration records never established a working replacement on the current pair.
Why the old tool was off the table
Gravity Sync was retired in July 2024 and its maintainer says the final release does not support Pi-hole 6 or later, because of architectural changes. The VM pair runs v6, so that rules out treating the old Gravity Sync setup as a supported replication path. It does not tell you how much the nodes had drifted, or whether some other sync mechanism was quietly running. The old evidence is still the record of what worked on the retired physical Pis; it just stopped answering the current question.
DNS health checks alone could not settle this either. Both nodes can answer a DNS query while holding different blocklists, groups, or local records. Keepalived manages ownership of the shared address; configuration replication has its own, separate failure modes. A passing query through the VIP only proves the node that currently owns it can serve DNS. It says nothing about whether the two nodes agree.
Choosing Nebula Sync
Nebula Sync uses the Pi-hole v6 API to synchronize a primary to replicas. It supports full Teleporter sync, selective configuration sync, and a cron schedule, and ships as a container as well as native binaries. Compose fits the lab’s existing container operations, so that was the deployment form I chose.
Orbital Sync was in the original comparison, but the repository was archived on March 31, 2025. Replacing one retired dependency with another doesn’t meet the maintenance bar. A small custom sync script would have left API authentication, scheduling, error handling, and selecting which settings to share for me to build and maintain; Nebula already does that job.
What deployed, 2026-10-04 into 2026-10-05
The live session ran from 21:58 UTC on 2026-10-04 to 04:07 UTC on 2026-10-05, when the first scheduled sync fired on its own. Everything below happened in that window, interactively, with my own confirmation at each step.
Selected Pi-hole policy now syncs hourly, at minute 07, from the designated primary to the replica. The sync runs from a transient container on the lab’s monitoring host, not the Portainer host I’d originally planned.
The two nodes’ downloaded gravity sets are byte-identical now: 72,234 domains each, matching SHA-256 hashes. Before this, they held 72,234 and 74,761 domains with different hashes, meaning the replica was serving a measurably different blocklist than the primary. Each node’s API now authenticates the sync over verified TLS, not a bypassed check.
I induced a real failure and watched it recover, specifically to prove the alert path rather than trust that a running container means replication works. Both the failure and the recovery reached Discord as alerts. A controlled VIP failover and failback passed afterward: the replica picked up the VIP, kept serving the synced policy and normal DNS while it held it, and the primary reclaimed the VIP on restart.
A continuous DNS-availability check ran through the TLS setup, the sync, the alert test, and the failover test: 7,901 samples over 134 minutes, zero sustained gaps. Three isolated single-query misses showed up, each one recovered on the very next sample. The check does not cover the VRRP password rotation itself, which I verified separately with direct checks.
I also rotated the VRRP shared password the two nodes use between themselves. The previous value had been exposed in public screenshots that are now removed; it’s rotated and the old value is gone.
What this closes out
The replication gap is closed, with a result to point to instead of a plan: deployed, alert path proven with a real failure, a failover tested under sync, and the exposed VRRP password rotated out. The host running the sync container ended up different from what I’d designed; everything else held.
Related posts: Moving HA DNS off the Raspberry Pis and onto Proxmox.