Requirements #
Before you start this guide, make sure you have
- 2 Piholes running on
- Ability to SSH to Servers Container/Pihole is running on
- If running in container, Using a Bind Mount to the host instead of a Docker Volume
In this Guide I have a Container Pihole running as primary on 12.12.12.12 and my backup will be a pihole running on a pi zero non container
Generate SSH Key #
generate and save to ~/.ssh/gravity
ssh-keygen -t rsa -C "gravity"
cat .ssh/gravity
cat .ssh/gravity.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeiMUeA9WVsKJHRbbPn3WIUPL9/w7N70lc1NL4hV20E7ugPcMeYACzOepSHiTDuiKx6rsrFrGR422FqZLDOLjgSMUqgU2jiFUMeNvNpM1hESn+UlEJo8JqUg9QPIU7wWBiSTv/saeFjdylhzz6CJBW5Lit7/EFxZJblHvN7vNcy8npMQyfzp9OPLhP1Bsaqlq/vmOJDb70an+ChFVje/1+CwVwMzE2buolqXYy5GlNk7xWaXUdXxPW1+Vvh4zfQ/dialUlXuyXGL06CbVTyLnPjojhWohfD5Xkr+WwckArF/tgGNcZ3J5jo4cmTaUez06fJ9kgTl92VXn+k3v++ZUAHYNZ91MAZ9pkHqf/g5GK9N61tnuyYGsd1p3wRNAwxsHFnZSrnMgma/+H+jTCdNxIKHYF+W6FRwTj4grfbR+mOAjDCbex90Axl1o5hu1i5+6s1xeWmtQ3OXXgj4AlDakYYxSDCAY4qRJdUJ4nQf+Yczsv2RYn6aUcD6jzVOQ7mFU= gravity
Add key to Primary Pihole Server #
- SSH into primary pihole server. If it is running in a container, ssh into the server the container is running on.
ssh USERNAME@IP_ADDRESS
- Add Public Key to ~/.ssh/authorized_keys
>> is import to add the key as a new line to the end of the file, > will replace everything in the file with the key
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeiMUeA9WVsKJHRbbPn3WIUPL9/w7N70lc1NL4hV20E7ugPcMeYACzOepSHiTDuiKx6rsrFrGR422FqZLDOLjgSMUqgU2jiFUMeNvNpM1hESn+UlEJo8JqUg9QPIU7wWBiSTv/saeFjdylhzz6CJBW5Lit7/EFxZJblHvN7vNcy8npMQyfzp9OPLhP1Bsaqlq/vmOJDb70an+ChFVje/1+CwVwMzE2buolqXYy5GlNk7xWaXUdXxPW1+Vvh4zfQ/dialUlXuyXGL06CbVTyLnPjojhWohfD5Xkr+WwckArF/tgGNcZ3J5jo4cmTaUez06fJ9kgTl92VXn+k3v++ZUAHYNZ91MAZ9pkHqf/g5GK9N61tnuyYGsd1p3wRNAwxsHFnZSrnMgma/+H+jTCdNxIKHYF+W6FRwTj4grfbR+mOAjDCbex90Axl1o5hu1i5+6s1xeWmtQ3OXXgj4AlDakYYxSDCAY4qRJdUJ4nQf+Yczsv2RYn6aUcD6jzVOQ7mFU= gravity" >> ~/.ssh/authorized_keys
Get IDS #
id
Take note of these, replace with your values
PRIMARY
| Name | Value |
|---|---|
| UID | 1000 |
| GID | 1000 |
| Groups | 1000 |
Now lets exit and ssh into our secondary that will pull config from primary
ssh $USERNAME@IP_ADDRESS
Repeat get ids
SECONDARY
| Name | Value |
|---|---|
| UID | 1000 |
| GID | 1003 |
| Groups | 1003 |
Install Gravity Sync #
https://github.com/vmstan/gravity-sync
We will make a custom config file so dont worry, just go through setup
curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash
Now lets update this file
Local is Secondary Pihole
Remote is Primary Pihole
/etc/gravity-sync/gravity-sync.conf
# REQUIRED SETTINGS ##########################
REMOTE_HOST='12.12.12.12' # IP OF PRIMARY
REMOTE_USER='neo' # PRIMARY USERNAME
# CUSTOM VARIABLES ###########################
# Pi-hole Folder/File Customization - Only need to be customized when using containers
LOCAL_PIHOLE_DIRECTORY='/etc/pihole' # SECONDARY DATA FOLDER
REMOTE_PIHOLE_DIRECTORY='/pihole/data' # PRIMARY DATA FOLDER
LOCAL_DNSMASQ_DIRECTORY=''
REMOTE_DNSMASQ_DIRECTORY=''
LOCAL_FILE_OWNER='1003:1003'
REMOTE_FILE_OWNER='1000:1000'
# Pi-hole Docker/Podman container name - Docker will pattern match anything set below
#LOCAL_DOCKER_CONTAINER='pihole-0'
REMOTE_DOCKER_CONTAINER='pihole-0' # IF CONTAINER
# HIDDEN FIGURES #############################
# See https://github.com/vmstan/gravity-sync/wiki/Hidden-Figures
Setup Gravity ssh key #
lets add the ssh key we generated to gravity sync
cat << 'EOF' > /etc/gravity-sync/gravity-sync.rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
THwxu2+qeI9FMAAAAHZ3Jhdml0eQECAwQ=
-----END OPENSSH PRIVATE KEY-----
EOF
Now it can access the primary server!
Create Sync script #
lets make a folder called ~/cronjobs that we will save this script in
mkdir ~/cronjobs
#!/usr/bin/env bash
install_if_not_exists() {
if ! command -v gravity-sync &> /dev/null; then
curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash
else
echo "gravity-sync already installed."
fi
}
# Function to log messages to a JSON file
log() {
mkdir -p $HOME/log
local log_file="$HOME/log/pihole_sync.log"
local message="$1"
local user=$(whoami)
local script_name=$(basename "$0")
local metadata='{"host": "'$(hostname)'", "user": "'"$user"'", "script": "'"$script_name"'", "timestamp": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'", "message": "'"$message"'"}'
echo "$metadata" >> "$log_file"
}
# Function to synchronize with gravity-sync and send notification
sync_and_notify() {
# Run gravity-sync pull
# IF command success, log success
# IF command fail, log fail
gravity-sync pull \
&& log "Sync Successful" \
|| log "Sync Failed"
}
# Main script starts here
# Call the sync_and_notify function
install_if_not_exists
sync_and_notify
Create Pull Cronjob #
lets create a new cronjob
crontab -e
Add this line to the bottom of the file
*/5 * * * * /home/{user}/cronjobs/pihole_sync.sh
This will run the script every 5 minutes
Run Script #
./cronjobs/pihole_sync.sh
gravity-sync already installed.
∞ Initializing Gravity Sync (4.0.7)
✓ Loading gravity-sync.conf
✓ Detecting local Pi-hole installation
✓ Detecting remote Pi-hole installation - docker
✓ Gravity Sync remote peer is configured
✓ Evaluating arguments: PULL
» Remote target neo@12.12.12.12
✓ Validating pathways to Pi-hole
✓ Validating pathways to DNSMASQ
✓ Hashing the remote Gravity Database
✓ Comparing to the local Gravity Database
✓ Hashing the remote DNS Records
✓ Comparing to the local DNS Records
✓ Comparing to the local DNS Records
✓ Hashing the remote DNS CNAMEs
✓ Comparing to the local DNS CNAMEs
! Static DHCP Addresses not detected on the local Pi-hole
! No replication is required at this time
∞ Gravity Sync PULL exited after 23 seconds
Check Log
cat log/pihole_sync.log | tail -n 5
{"host": "zero", "user": "neo", "script": "pihole_sync.sh", "timestamp": "2024-05-14T01:58:59Z", "message": "Sync Successful"}