Building a Highly Available Pi-hole Setup with Keepalived and Nebula

The Challenge

When I first set up Pi-hole on a single Raspberry Pi to block ads across my network, everything worked fine. But as my network grew, I realized that a single Pi-hole instance was vulnerable to downtime. If the server went down, my entire network would lose ad-blocking, which was a non-starter. I needed a solution that would ensure high availability and redundancy for my Pi-hole setup.

The Goal

My goal was to build a Pi-hole setup that would:

  • Automatically switch between Pi-hole servers in case one goes down, with failover functionality.
  • Keep my configurations synced between the two Pi-hole servers.
  • Ensure minimal manual intervention if something went wrong.

After some research and trial-and-error, I decided to use Keepalived for failover and Nebula to sync the servers. Here’s how I made it work.


The Working Solution

I went with two Pi-hole instances running on separate servers, and used Keepalived to manage a Virtual IP (VIP) that both Pi-hole servers would share. This way, if one Pi-hole instance failed, the other would automatically take over, ensuring that DNS requests continue without interruption

1. Keepalived for Failover

Keepalived is a tool that manages failover by assigning a virtual IP to a primary and secondary server. If the primary Pi-hole server fails, the virtual IP moves to the secondary server, making it the new active server. This means that clients (like my devices) always use the same DNS IP, and they don’t need to know which Pi-hole server is active.

Here’s a basic Keepalived configuration for the two Pi-hole servers:

Primary/Master:

vrrp_instance VI_1 {
    state MASTER
    interface eth0                    # Change if your interface name is different
    virtual_router_id 51
    priority 101                       # Higher priority for master node
    advert_int 1                        # Advertisement interval (seconds)
    authentication {
        auth_type PASS
        auth_pass 1234                  # Authentication password (same on both nodes)
    }
    virtual_ipaddress {
        192.168.178.253                # Virtual IP (VIP)
    }
}

Backup/Replica:

vrrp_instance VI_1 {
    state BACKUP
    interface eth0                    # Same interface name as the Master
    virtual_router_id 51
    priority 100                       # Lower priority for backup node
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1234                  # Same password as on the Master
    }
    virtual_ipaddress {
        192.168.178.253                # Virtual IP (VIP)
    }
}

2. Nebula for Server Sync

To keep my Pi-hole configurations consistent across both servers, I set up Nebula Sync, a lightweight App that securely connects my two Pi-hole servers. Nebula enables me to sync settings, including blocklists, whitelist configurations, and DNS entries.

With Nebula, whenever I update the settings on one Pi-hole server, the changes are automatically replicated to the other instance, ensuring both servers stay in sync without requiring manual configuration updates.

Here’s my Nebula Sync Kubernetes manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nebula
  namespace: nebula
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nebula
  template:
    metadata:
      labels:
        app: nebula
    spec:
      containers:
        - name: nebula
          image: ghcr.io/lovelaze/nebula-sync:v0.9.0
          env:
            - name: PRIMARY
              value: "http://192.168.178.200|<Secure-password>"
            - name: REPLICAS
              value: "http://192.168.178.181|<Secure-password>"
            - name: FULL_SYNC
              value: "true"
            - name: RUN_GRAVITY
              value: "true"
            - name: CRON
              value: "0 * * * *"
            - name: TZ
              value: "Europe/Berlin"
            - name: CLIENT_SKIP_TLS_VERIFICATION
              value: "true"

The Final Setup

To summarize, here’s what my setup looks like:

  1. Two Pi-hole Servers: Both Pi-hole instances are set up on separate machines to ensure redundancy.
  2. Keepalived for Failover: I configured Keepalived to manage a virtual IP (VIP) that always points to the active Pi-hole server. If one server goes down, the other takes over automatically.
  3. Nebula for Synchronization: Nebula keeps both Pi-hole servers in sync by securely communicating between them and ensuring that updates to one server are mirrored on the other.

Final Thoughts

This setup has given me peace of mind, knowing that my network is always protected from ads and trackers. Even if one Pi-hole server fails, my devices continue to get the same level of protection without any disruption. If you’re running Pi-hole in your home or office and want to ensure that it remains highly available, I highly recommend looking into Keepalived and Nebula for failover and synchronization.

Let me know if you have any questions or need help with the setup — I’d be happy to share more details!