High Availability DNS with PiHole, KeepAlived & Gravity Sync

Today we're going to set up a high availability pi hole cluster, with 3 nodes that keep in sync with one another and fail over to a VIP if one or two of them go down.
Let's get started.
Installation
Keepalived provides frameworks for both load balancing and high availability. The load balancing framework relies on the well-known and widely used Linux Virtual Server (IPVS) kernel module, which provides Layer 4 load balancing.
sudo apt update
sudo apt install keepalived
sudo apt install libipset13
Next we need to locate the ip address of each one of our pi-hole nodes, we can do this by using ip a
which will give us our IP and interface.
Next we need to set out a config file, we currently don't have one so we can create one by doing the following:
sudo nano /etc/keepalived/keepalived.conf
And paste the following into each file, just make sure to separate the file into their own files.
# NODE 1 ----------------------------
vrrp_instance PI_HOLE {
state MASTER
interface ens18
virtual_router_id 84
priority 200
advert_int 1
authentication {
auth_type PASS
auth_pass xxxx # some 8 digit password
}
virtual_ipaddress {
192.168.4.100/22
}
}
# NODE 2 ----------------------------
vrrp_instance PI_HOLE {
state BACKUP
interface ens18
virtual_router_id 84
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass xxxx # some 8 digit password
}
virtual_ipaddress {
192.168.4.100/22
}
}
# NODE 3 ----------------------------
vrrp_instance PI_HOLE {
state BACKUP
interface ens18
virtual_router_id 84
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass xxxx # some 8 digit password
}
virtual_ipaddress {
192.168.4.100/22
}
}
With the above we're setting our VIP to be 192.168.4.100
this will become our secondary DNS server IP. Make a note of the priority too, the higher the number the better the priority.
Now it's time to start the keepalived
service
sudo systemctl enable --now keepalived.service
Confirm it's running
sudo systemctl status keepalived.service
Let's check that our VIP works, open a terminal window and divide it into three, ssh into each one of your nodes and while pinging the VIP run the following on each node one by one.
sudo systemctl stop keepalived.service
As you're stopping the service you will see the ping continue, until you stop the service on the last node where it will fail. Confirming that after stopping the service on two nodes, we're still getting a successful ping on our VIP until the last node.
Let's start up the service again
sudo systemctl start keepalived.service
Keeping everything in sync
What's the point of having high availability if we can't keep our pi-hole nodes in sync with each other. We can solve this by using Gravity Sync. This allows us to keep our pi-hole nodes in sync with each other.
Setup
Open an SSH session to each Pi-hole, and execute the following script.
curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash
The installer will validate that you have all the necessary components to use Gravity Sync, including an active Pi-hole deployment. It will also add a password-less sudo configuration file for the current user if it is required.
Make sure you have run the installation script on each Pi-hole node before starting the configuration.
During the setup, you will be prompted for the following:
- The IP address of the remote Pi-hole instance.
- The SSH username to connect to your remote Pi-hole host.
The configuration will be saved as gravity-sync.conf
in /etc/gravity-sync
. If you need to make adjustments to your settings in the future.
Now lets get our sync on. To run a comparison between remote and local databases, which will be non-disruptive, and see if everything has been configured correctly. Do this from all Pi-hole nodes.
gravity-sync compare
Once complete we can run our first sync. Pick the Pi-hole instance that currently has the "authoritative" list of settings and run the following command to send all of those settings to its peer for the first time. Do this only from the authoritative Pi-hole.
Now let's automate things, the automation of sync is accomplished by adding an execution of the script to each host's system configuration. Simply run:
gravity-sync auto hour
Which will automatically sync every hour. And there we have it a high availability DNS cluster, all that is left is to set up your router with the DNS server's IPs.
Personally, I have done the following:
- Main DNS: The VIP
- Secondary DNS: cloudflare DNS
Using the VIP as main DNS means I have 3 nodes to fail before my DNS service goes down. Which is great, I have 3 nodes on each one of my Proxmox nodes which is in a HA cluster, so this way I have a high availability hypervisor cluster with a high availability pihole.