# Looking for feedback on HA pihole setup with unbound + keepalived + gravitysync

**URL:** <https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944>\
**Category:** Customizing Pi-hole\
**Created:** [January 24, 2022, 10:51am UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944 "2022-01-24T10:51:39Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![floz](https://discourse-cdn.pi-hole.net/letter_avatar_proxy/v4/letter/f/51bf81/32.png) [@floz](https://discourse.pi-hole.net/u/floz)\
**Post date:** [January 24, 2022, 10:51am UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/1 "2022-01-24T10:51:39Z")

</div>

> Moderator edit:  
> This post contains scripts, or links to scripts, that are untested by the Pi-hole team. We encourage users to be creative, but we are unable to support any potential problems caused by running these scripts

Hello everyone,

this is my first post here and I hope I chose the right category. I wasn't sure if the topic is more suitable for general/customizing pihole or help/community or maybe somewhere completely different.

I've read a lot on raspberry pis lately and was looking for a nice project that would keep me occupied for some time, so I decided I wanted to set up a dual pihole HA setup. After reading a lot on it (also in this forum), I decided to go with two raspi zeros with USB2LAN adapters directly connected to my AVM Fritzbox 7590 and to use pihole, unbound, keepalived and gravitysync by @vmstan.

I know that it may be a little overkill, however, I needed a project to spend some time on...

I would like to show you my final setup with the instructions I wrote for myself and would really appreciate any kind of feedback, suggestions for improvement or security/performance concerns, as I'm really a beginner when it comes to network administration, DNS and linux.

Maybe it can even help another beginner to start their project.

**1. Raspberry Pi OS: Installation and configuration (same for both raspis)**

- Write Raspberry Pi OS Lite (bullseye) image on SD-card using Raspberry Pi Imager or any other suitable tool
- Create `ssh` file on the SD-card to enable ssh access to the raspi
- Insert SD-card into raspi and boot up
- Check IP adress in the router and assign a static IP
- ssh into the raspi via Windows Powershell or putty using `ssh pi@xxx.xxx.xxx.xxx` where xxx.xxx.xxx.xxx is the ip adress of the raspi
- Open config with `sudo raspi-config` to change hostname, password, timezone and expand the filesystem. Reboot.
- Update packages with `sudo apt update && sudo apt upgrade`

**2. Pihole: installation and configuration (same for both raspis)**  
Command from the official pihole documentation [GitHub - pi-hole/pi-hole: A black hole for Internet advertisements](https://github.com/pi-hole/pi-hole/#one-step-automated-install)

- `curl -sSL https://install.pi-hole.net | bash`
- Use standard values during installation
- Log into the pihole web userinterface with `http://xxx.xxx.xxx.xxx/admin` and go to Groupmanagement -\> Adlists and add adlists (good lists for example under [https://firebog.net/](https://firebog.net/)). You only need to do this for your primary raspi/pihole, because we will sync the lists with the secondary raspi/pihole later on.

**3. Unbound: installation and configuration (same for both raspis)**  
Follow the official installation instructions from the pihole documentation [unbound - Pi-hole documentation](https://docs.pi-hole.net/guides/dns/unbound/)

- Install unbound with `sudo apt install unbound`
- Open unbound config with `sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf` and insert config from the documentation
- If you use Raspberry Pi OS bullseye you need to open `sudo nano /etc/resolvconf.conf` and comment out the line starting with `unbound_conf` and delete the resolvconf\_resolver.conf with `sudo rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf`. Further information on this topic can be found here: [https://discourse.pi-hole.net/t/warning-raspbian-october-2021-release-bullseye-unbound](https://discourse.pi-hole.net/t/warning-raspbian-october-2021-release-bullseye-unbound)
- Restart unbound with new config using `sudo systemctl restart unbound`
- Check that unbound works correctly with `dig pi-hole.net @127.0.0.1 -p 5335`, `dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335` and `dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335` which should return NOERROR, SERVFAIL and NOERROR respectively
- Log into the pihole web userinterface with `http://xxx.xxx.xxx.xxx/admin` and go to Settings -\> DNS to unselect the standard DNS servers and insert `127.0.0.1#5335` in Custom DNS 1 (IPv4)

**4. gravitysync: Installation and configuration**  
Instructions from official installation instructions [GitHub - vmstan/gravity-sync: 💫 The easy way to synchronize the DNS configuration of two Pi-hole 5.x instances.](https://github.com/vmstan/gravity-sync)

For the primary raspi/pihole:

- Install gravitysync using `export GS_INSTALL=primary && curl -sSL https://gravity.vmstan.com | bash`

For the secondary raspi/pihole:

- Install gravitysync using `export GS_INSTALL=secondary && curl -sSL https://gravity.vmstan.com | bash` and when asked enter password of secondary raspi, IP-adress of primary raspi, username of primary raspi and password of primary raspi
- Go to gravitsync folder with `cd ./gravity-sync` and check if gravitysync is working correctly with `./gravity-sync.sh compare`
- Pull databases from primary raspi/pihole with `./gravity-sync.sh pull`
- Activate automatic sync with `./gravity-sync.sh automate` and define the synchronization frequency when asked

**5. postfix: Installation and configuration (same for both raspis)**  
In this setup postfix is used with a gmail account to allow keepalived (see 6.) to send mails if one of the raspis/piholes goes down and the other one takes over. There are probably a lot of other option to do this.

- Install postfix and some sasl package with `sudo apt install postfix libsasl2-modules` and select "Internet Site" when asked during installation
- Open postfix config with `sudo nano /etc/postfix/main.cf` and change `relayhost =` to `relayhost = [smtp.gmail.com]:587` and `inet_interfaces = all` to `inet_interfaces = loopback-only`
- Add the following lines at the bottom of the config

> smtp\_sasl\_auth\_enable = yes  
> smtp\_sasl\_security\_options = noanonymous  
> smtp\_sasl\_password\_maps = hash:/etc/postfix/sasl\_passwd  
> smtp\_tls\_security\_level = encrypt  
> smtp\_tls\_CAfile = /etc/ssl/certs/ca-certificates.crt

- Create the file containing the login data for gmail with `sudo nano /etc/postfix/sasl_passwd` and insert

> [smtp.gmail.com]:587 USERNAME@gmail.com:APPPASSWORD

where USERNAME is the gmail username and APPPASSWORD is the app password you can create in the gmail web interface under account settings

- Create a .db file out of the login data file, that postfix needs for some reason with `sudo postmap /etc/postfix/sasl_passwd`
- Secure the files by restricting access as the paswords are written in the files in plain text using `sudo chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db`
- Restart postfix service with new config using `sudo systemctl restart postfix`

**6. keepalived: Installation and configuration**  
On primary raspi/pihole:

- Install keepalived with `sudo apt install keepalived`
- Open config with `sudo nano /etc/keepalived/keepalived.conf` and insert

```auto
global_defs {
  router_id HOSTNAME #HOSTNAME: hostname of primary pihole
  notification_email {
      USERNAME@gmail.com #USERNAME: gmail-username
  }
  smtp_server localhost
  smtp_connect_timeout 30
}

vrrp_track_process track_pihole {
  process pihole-FTL
  weight 50
}

vrrp_instance pihole1 {
   state MASTER
   interface eth0
   virtual_router_id 51
   priority 100
   advert_int 1
   smtp_alert

   unicast_src_ip PRIMARY-PIHOLE-IPADRESS	#PRIMARY-PIHOLE-IPADRESS: xxx.xxx.xxx.xxx
   unicast_peer {
       SECONDARY-PIHOLE-IPADRESS #SECONDARY-PIHOLE-IPADRESS: xxx.xxx.xxx.xxx
   }
   authentication {
       auth_type PASS
       auth_pass 8-CHARACTER-PASSWORD #8-CHARACTER-PASSWORD: alphanumerical password with max. 8 chars
   }
   virtual_ipaddress {
      VIRTUAL-IPADRESS #VIRTUAL-IPADRESS: xxx.xxx.xxx.xxx/24
   }
   track_process {
      track_pihole
   }
}

```

- Enable autostart of keepalived service with `sudo systemctl enable --now keepalived` and restart keepalived with `sudo systemctl restart keepalived`

On secondary raspi/pihole:

- Install keepalived with `sudo apt install keepalived`
- Open config with `sudo nano /etc/keepalived/keepalived.conf` and insert

```auto
global_defs {
  router_id HOSTNAME #HOSTNAME: hostname of primary pihole
  notification_email {
      USERNAME@gmail.com #USERNAME: gmail-username
  }
  smtp_server localhost
  smtp_connect_timeout 30
}

vrrp_track_process track_pihole {
  process pihole-FTL
  weight 50
}

vrrp_instance pihole2 {
   state BACKUP
   interface eth0
   virtual_router_id 51
   priority 90
   advert_int 1
   smtp_alert

   unicast_src_ip Secondary-PIHOLE-IPADRESS	#SECONDARY-PIHOLE-IPADRESS: xxx.xxx.xxx.xxx
   unicast_peer {
       PRIMARY-PIHOLE-IPADRESS #PRIMARY-PIHOLE-IPADRESS: xxx.xxx.xxx.xxx
   }
   authentication {
       auth_type PASS
       auth_pass 8-CHARACTER-PASSWORD #8-CHARACTER-PASSWORD: alphanumerical password with max. 8 chars
   }
   virtual_ipaddress {
      VIRTUAL-IPADRESS #VIRTUAL-IPADRESS: xxx.xxx.xxx.xxx/24
   }
   track_process {
      track_pihole
   }
}

```

- Enable autostart of keepalived service with `sudo systemctl enable --now keepalived` and restart keepalived with `sudo systemctl restart keepalived`

That's it! Just define the virtual IP-adress as DNS server in your router. Pihole, unbound and gravitysync work fine so far. But keepalived only switches to the secondary pihole if the primary pihole looses network connection or powers of. The vrrp\_track\_process is not working, unfortunately. If I stop the pihole DNS service on the primary pihole, keepalived will not switch to the secondary pihole. I'm still working on that. Maybe someone here has an idea how to implement this functionality.

Thanks in advance for reading all of this and any feedback you can offer.

---

<div class="post-metadata">

**Author:** ![yubiuser](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/yubiuser/32/12100_2.png) [@yubiuser](https://discourse.pi-hole.net/u/yubiuser)\
**Post date:** [January 24, 2022, 4:18pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/2 "2022-01-24T16:18:26Z")

</div>

> [@floz](#):
>
> I needed a project to spend some time on...

If you still have some time left, we are always looking for users contributing code, fixing bugs or improving documentation 😉

---

<div class="post-metadata">

**Author:** ![CesarCamacho](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/cesarcamacho/32/28663_2.png) [@CesarCamacho](https://discourse.pi-hole.net/u/CesarCamacho)\
**Post date:** [January 24, 2022, 5:35pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/4 "2022-01-24T17:35:24Z")

</div>

> [@floz](#):
>
> keepalived

thanks floz for sharing, I use a similar configuration I also use unbound + gravity-sync and currently I have problems my primary pihole freezes at 15 hours UTC, I see that you implemented keepalived I don't know what it is used for?, thanks for sharing your configuration is Very helpful, I was looking for this configuration for a long time and it is hardly documented, I use this configuration for 400 clients with 30000 queries

---

<div class="post-metadata">

**Author:** ![CesarCamacho](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/cesarcamacho/32/28663_2.png) [@CesarCamacho](https://discourse.pi-hole.net/u/CesarCamacho)\
**Post date:** [January 24, 2022, 5:44pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/5 "2022-01-24T17:44:12Z")

</div>

I share a link to configure gravity-sync, I don't know if it helps

> **[Create a pihole High Availability setup with 2 pihole instances on Proxmox...](https://florianmuller.com/create-a-pihole-high-availability-setup-with-2-pihole-instances-on-proxmox-and-gravity-sync)**
>
> In this guide we will cover how to create a High Availability pihole setup with Gravity Sync, so next time one of your pihole is not reachable, your DNS requests are still resolving over the fallback pihole instance.

---

<div class="post-metadata">

**Author:** ![floz](https://discourse-cdn.pi-hole.net/letter_avatar_proxy/v4/letter/f/51bf81/32.png) [@floz](https://discourse.pi-hole.net/u/floz)\
**Post date:** [January 24, 2022, 7:35pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/6 "2022-01-24T19:35:59Z")

</div>

> [@yubiuser](#):
>
> If you still have some time left, we are always looking for users contributing code, fixing bugs or improving documentation 😉

Thanks for the offer. Will definitely think about it. Sounds interesting.

> [@CesarCamacho](#):
>
> I share a link to configure gravity-sync, I don't know if it helps

Thanks for the link. I will have a look at it. Maybe there's something interesting in it.

> [@CesarCamacho](#):
>
> I see that you implemented keepalived I don't know what it is used for?

> **[Keepalived for Linux](https://keepalived.org/)**
>
> Keepalived provides robust High-Availability and Load Balancing features for Linux critical infrastructures

I use keepalived for high availability. So there will be a virtual IP adress that will point to the primary pihole as long as it's working correctly. So all DNS requests will got to the primary pihole. But when it goes down, keepalived will switch the virtual IP adress to the secondary pihole. This has the advantage for me, that I have all my pihole statistics in one place on the primary pihole. But keepalived can also do load balancing. So maybe that's also interesting for you to make your network more efficient.

---

<div class="post-metadata">

**Author:** ![CesarCamacho](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/cesarcamacho/32/28663_2.png) [@CesarCamacho](https://discourse.pi-hole.net/u/CesarCamacho)\
**Post date:** [January 24, 2022, 8:41pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/7 "2022-01-24T20:41:02Z")

</div>

ok thank you very much, I will start to review and implement some things that I need and then I will be commenting to you in case I have problems

---

<div class="post-metadata">

**Author:** ![CesarCamacho](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/cesarcamacho/32/28663_2.png) [@CesarCamacho](https://discourse.pi-hole.net/u/CesarCamacho)\
**Post date:** [January 26, 2022, 3:21am UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/8 "2022-01-26T03:21:18Z")

</div>

I have implemented Keepalived and it is great. Thanks for your contribution. I would like to ask a question. This can be implemented for IPv6. I see that only the virtual IP is only for IPV4.

---

<div class="post-metadata">

**Author:** ![floz](https://discourse-cdn.pi-hole.net/letter_avatar_proxy/v4/letter/f/51bf81/32.png) [@floz](https://discourse.pi-hole.net/u/floz)\
**Post date:** [January 27, 2022, 8:54am UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/9 "2022-01-27T08:54:49Z")

</div>

I only used IPv4 but you can use keepalived also with IPv6 in the same way. However, you can not use both IPv4 and IPv6 addresses as virtual\_ipaddresses in a single vrrp\_instance. If you want to use both, IPv4 and IPv6, you could define a vrrp\_sync\_group with two vrrp\_instance blocks, one for IPv4 and one for IPv6.

Hope that helps.

---

<div class="post-metadata">

**Author:** ![CesarCamacho](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/cesarcamacho/32/28663_2.png) [@CesarCamacho](https://discourse.pi-hole.net/u/CesarCamacho)\
**Post date:** [January 27, 2022, 11:18am UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/10 "2022-01-27T11:18:27Z")

</div>

Well, could you help me by explaining how to do it? Well, I kept using ivp6 of the primary and secondary server

---

<div class="post-metadata">

**Author:** ![Joe\_Schubert](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/joe_schubert/32/10791_2.png) [@Joe\_Schubert](https://discourse.pi-hole.net/u/Joe_Schubert)\
**Post date:** [December 3, 2022, 8:38pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/11 "2022-12-03T20:38:26Z")

</div>

I know this is an older topic and I am necro-ing it... But, I just wanted to stop by and say thank you for the amazing writeup.

The reason I think that your vrrp\_track\_process isn't working is that even when pihole is disabled is that pihole-FTL is still sleeping, and the process doesn't exit to trigger that tracker. I personally only need this for a while a container or server is rebooting, so the process _will_ be exited and the interface will be down lol.

However, I'd suggest that if you really need the functionality for it to failover while pihole-ftl is not running you should look into vrrp\_script. A simple script that runs pihole status and greps/awk/whatever else for string  
" [✓] Pi-hole blocking is enabled"  
and returns successful should achieve what you were going for in your vrrp\_track\_process block.

---

<div class="post-metadata">

**Author:** ![Petr\_Babarykin](https://discourse-cdn.pi-hole.net/user_avatar/discourse.pi-hole.net/petr_babarykin/32/40275_2.png) [@Petr\_Babarykin](https://discourse.pi-hole.net/u/Petr_Babarykin)\
**Post date:** [February 3, 2024, 10:36pm UTC](https://discourse.pi-hole.net/t/looking-for-feedback-on-ha-pihole-setup-with-unbound-keepalived-gravitysync/52944/12 "2024-02-03T22:36:26Z")

</div>

floz, thank you for your instructions.  
Joe\_Schubert , you are spot on. However, I had hard time to understand what and how to change. So after some more research, here is a simple guide with two changes in "keepalived.conf" at step 6 of the instructions above:

1. Open config with `sudo nano /etc/keepalived/keepalived.conf` and replace  
"  
vrrp\_track\_process track\_pihole {  
process pihole-FTL  
weight 50  
}  
"  
with  
"  
vrrp\_script track\_pihole {  
script "/usr/bin/pgrep pihole"  
interval 2  
}  
"
2. then replace "  
track\_process {  
track\_pihole  
}  
"  
with  
"  
track\_script {  
track\_pihole  
}  
"  
One can change "2" to "1" or a different number of seconds at "interval 2" to run the check.  
Source: I found this discussion [track\_process not working on my Raspberry Pi · Issue #1708 · acassen/keepalived · GitHub](https://github.com/acassen/keepalived/issues/1708) helpful, which also suggests that one could recompile the kernel and get track\_process work as intended ("enable CONFIG\_CONNECTOR via menuconfig to alter default .config").
