Gravity Sync + Keepalived (Virtual DNS IP)
This document describes how to build a high‑availability Pi-hole DNS setup using:
Clients and the router only use one DNS IP, while failover happens automatically.
| Component | IP Address | Role |
|---|---|---|
| Pi-hole DNS1 | 192.168.2.3 |
Primary node (preferred master) |
| Pi-hole DNS2 | 192.168.2.4 |
Secondary node (backup) |
| Virtual DNS IP (VIP) | 192.168.2.5 |
Only DNS used by router/clients |
Gravity Sync ensures that:
…are always identical on both Pi-holes.
curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash
Verify installation:
gravity-sync version
192.168.2.3):gravity-sync config
Enter:
192.168.2.4thomas)22192.168.2.4):gravity-sync config
Enter:
192.168.2.3On DNS1 (authoritative node):
gravity-sync compare
gravity-sync push
Enable automatic sync:
gravity-sync auto
Keepalived provides a single shared DNS IP that moves between nodes.
sudo apt update
sudo apt install keepalived -y
Create on both nodes:
sudo nano /usr/local/bin/check_pihole.sh
#!/bin/bash
if systemctl is-active --quiet pihole-FTL; then
exit 0
else
exit 1
fi
sudo chmod +x /usr/local/bin/check_pihole.sh
Add at the top of /etc/keepalived/keepalived.conf (both nodes):
global_defs {
script_user root
enable_script_security
}
vrrp_script chk_pihole {
script "/usr/local/bin/check_pihole.sh"
interval 5
fall 2
rise 2
weight -50
}
vrrp_instance VI_PIHOLE {
state MASTER
interface enp0s18
virtual_router_id 42
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass piholevip
}
virtual_ipaddress {
192.168.2.5/24
}
track_script {
chk_pihole
}
}
vrrp_script chk_pihole {
script "/usr/local/bin/check_pihole.sh"
interval 5
fall 2
rise 2
weight -50
}
vrrp_instance VI_PIHOLE {
state BACKUP
interface enp0s18
virtual_router_id 42
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass piholevip
}
virtual_ipaddress {
192.168.2.5/24
}
track_script {
chk_pihole
}
}
🔧 Replace
enp0s18with the correct interface name (ip -o -4 addr show).
sudo systemctl enable keepalived
sudo systemctl restart keepalived
sudo systemctl status keepalived
Check VIP:
ip a | grep 192.168.2.5
Stop Keepalived on DNS1:
sudo systemctl stop keepalived
Verify VIP moved to DNS2:
ip a | grep 192.168.2.5
Restart DNS1:
sudo systemctl start keepalived
Configure ONLY ONE DNS server:
DNS: 192.168.2.5
Do NOT add .3 or .4 to the router.
ip -o -4 addr show
journalctl -u keepalived -f
gravity-sync info
gravity-sync logs
✔ Single DNS IP for entire network
✔ Automatic failover
✔ Fully synchronized Pi-hole configuration
✔ No manual duplication of rules
✔ Near-zero downtime
Maintained for TuxiNet / DockMost