Homelab Pi-hole redundant betreiben: Keepalived für eine gemeinsame VIP und Nebula Sync für die Datenbankreplikation – so geht HA-DNS im Homelab. 2026-03-04T00:00:00.000Z 8 pi-hole,keepalived,nebula-sync,dns,homelab,self-hosted,high-availability
← Alle Posts

Hochverfügbares Pi-hole mit Keepalived und Nebula Sync

Pi-hole redundant betreiben: Keepalived für eine gemeinsame VIP und Nebula Sync für die Datenbankreplikation – so geht HA-DNS im Homelab.

4. März 2026 8 min Lesezeit
pi-holekeepalivednebula-syncdnshomelabself-hostedhigh-availability

TL;DR – Zwei Pi-hole-Instanzen, eine virtuelle IP via Keepalived (VRRP), Datenbanksynchronisation via Nebula Sync. Fällt ein Node aus, übernimmt der andere die VIP innerhalb von Sekunden. Gravity Sync ist tot – Nebula Sync ist der Nachfolger.

Ein einzelnes Pi-hole ist die Achillesferse jedes Homelabs, das darauf angewiesen ist. Startet der Host neu, gibt es ein Update, oder der Raspberry Pi zieht einfach mal die Grätsche – und plötzlich geht im ganzen Netz nichts mehr, weil DNS fehlt. Die Lösung ist eine hochverfügbare Konfiguration mit zwei Pi-hole-Instanzen, einer gemeinsamen virtuellen IP und einer synchronisierten Datenbank.

Früher war Gravity Sync das Mittel der Wahl für die Replikation. Das Projekt ist jedoch seit längerem inaktiv und wird nicht mehr weiterentwickelt. Der aktuelle Nachfolger heißt Nebula Sync – ein in Go geschriebenes Tool, das die Pi-hole-API nutzt und deutlich moderner aufgestellt ist.

Architektur und Voraussetzungen

Die Grundidee ist simpel: Zwei Hosts laufen parallel, beide mit Pi-hole. Keepalived verwaltet eine Virtual IP (VIP) via VRRP. Alle Clients im Netz bekommen diese VIP als DNS-Server eingetragen – nicht die individuellen IPs der Hosts. Fällt der primäre Node aus, übernimmt der Standby-Node die VIP innerhalb weniger Sekunden.

Für dieses Setup braucht man:

  • Zwei Hosts (Raspberry Pi, VM, LXC-Container – egal) im selben L2-Segment
  • Pi-hole auf beiden Hosts installiert
  • Keepalived auf beiden Hosts
  • Nebula Sync auf einem der Hosts (oder als separater Container)
  • Eine freie IP im Subnetz für die VIP

Beispiel-IPs für diesen Artikel:

  • Node 1 (Primary): 192.168.1.10
  • Node 2 (Secondary): 192.168.1.11
  • VIP: 192.168.1.10

SCREENSHOT: Netzwerktopologie mit zwei Pi-hole-Nodes und Keepalived VIP Beide Nodes laufen aktiv, die VIP liegt auf dem Primary. Bei Ausfall übernimmt der Secondary.

Pi-hole installieren

Auf beiden Nodes Pi-hole installieren – entweder via dem klassischen Installer oder als Docker-Container. Wichtig: Auf Node 1 wird Pi-hole mit der echten IP 192.168.1.10 konfiguriert, auf Node 2 mit 192.168.1.11. Die VIP 192.168.1.5 kommt erst durch Keepalived dazu.

Für Docker-basierte Setups empfiehlt sich folgende Compose-Datei (auf beiden Nodes identisch, nur die IP anpassen):

services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    network_mode: host
    environment:
      TZ: Europe/Berlin
      WEBPASSWORD: "sicheres-passwort"
      FTLCONF_LOCAL_IPV4: "192.168.1.10" # Auf Node 2: 192.168.1.11
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    restart: unless-stopped

network_mode: host ist hier wichtig, damit Keepalived später die VIP direkt auf dem Interface des Hosts binden kann und Pi-hole darauf lauscht.

Keepalived konfigurieren

Keepalived auf beiden Nodes installieren:

sudo apt update && sudo apt install keepalived -y

Konfiguration auf Node 1 (Primary)/etc/keepalived/keepalived.conf:

vrrp_script chk_pihole {
    script "/usr/bin/pgrep pihole-FTL"
    interval 2
    weight -20
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 110
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass geheimes-passwort
    }

    virtual_ipaddress {
        192.168.1.5/24
    }

    track_script {
        chk_pihole
    }
}

Konfiguration auf Node 2 (Secondary) – identisch, aber mit state BACKUP und priority 100:

vrrp_script chk_pihole {
    script "/usr/bin/pgrep pihole-FTL"
    interval 2
    weight -20
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass geheimes-passwort
    }

    virtual_ipaddress {
        192.168.1.5/24
    }

    track_script {
        chk_pihole
    }
}

Der vrrp_script-Block ist entscheidend: Er prüft, ob pihole-FTL läuft. Ist das nicht der Fall, sinkt die Priorität des Nodes um 20 Punkte – der Secondary übernimmt automatisch die VIP.

Keepalived auf beiden Nodes starten:

sudo systemctl enable keepalived --now

Mit ip addr show eth0 auf Node 1 sollte die VIP 192.168.1.5 jetzt sichtbar sein.

Nebula Sync einrichten

Nebula Sync repliziert die Pi-hole-Konfiguration (Blocklisten, Allowlists, DNS-Einträge, Einstellungen) zwischen den Instanzen. Es nutzt die Pi-hole API v6 und ist damit kompatibel mit Pi-hole v6+.

Hinweis: Gravity Sync ist seit 2023 nicht mehr aktiv gepflegt und funktioniert mit Pi-hole v6 nicht mehr zuverlässig. Nebula Sync ist der offizielle Community-Nachfolger.

Nebula Sync lässt sich am einfachsten als Docker-Container betreiben – entweder auf einem der Pi-hole-Hosts oder auf einem separaten System:

services:
  nebula-sync:
    image: ghcr.io/lovelaze/nebula-sync:latest
    container_name: nebula-sync
    environment:
      PRIMARY: "http://192.168.1.10|admin-passwort-node1"
      REPLICAS: "http://192.168.1.11|admin-passwort-node2"
      FULL_SYNC: "true"
      CRON: "*/5 * * * *"
      TZ: Europe/Berlin
    restart: unless-stopped

Die Umgebungsvariablen im Überblick:

  • PRIMARY: URL und Passwort der primären Pi-hole-Instanz (Format: URL|Passwort)
  • REPLICAS: URL und Passwort der Replikat-Instanz(en), kommagetrennt für mehrere
  • FULL_SYNC: Synchronisiert alle Einstellungen, nicht nur Gravity
  • CRON: Sync-Intervall – alle 5 Minuten ist ein guter Startwert

Nach dem Start kann man die Logs prüfen:

docker logs nebula-sync -f

Ein erfolgreicher Sync sieht etwa so aus:

INFO Sync started
INFO Syncing gravity from primary to replica
INFO Syncing settings from primary to replica
INFO Sync completed successfully

DNS-Konfiguration und Clients

Alle Clients im Netz bekommen jetzt ausschließlich die VIP 192.168.1.5 als DNS-Server eingetragen – am besten über den DHCP-Server (Router, OPNsense, etc.). Die individuellen IPs der Nodes spielen für die Clients keine Rolle mehr.

Optional kann man als sekundären DNS-Server einen öffentlichen Resolver wie 1.1.1.1 eintragen – als letzten Fallback, falls beide Pi-hole-Nodes gleichzeitig ausfallen. Das ist ein Kompromiss zwischen Verfügbarkeit und konsequentem Ad-Blocking.

Für OPNsense unter Services > DHCPv4 > [Interface]:

DNS Server 1: 192.168.1.5
DNS Server 2: 1.1.1.1  # Optional, als Fallback

Einen schnellen Test macht man mit:

# Failover testen: Pi-hole auf Node 1 stoppen
sudo systemctl stop pihole-FTL  # oder: docker stop pihole

# VIP sollte jetzt auf Node 2 liegen
ping 192.168.1.5

# DNS-Auflösung testen
dig @192.168.1.5 example.com

Fazit

Das Setup ist überschaubar in der Komplexität, aber der Gewinn an Zuverlässigkeit ist enorm. Keepalived übernimmt die VIP-Verwaltung zuverlässig und schnell, Nebula Sync hält die Konfiguration konsistent. Wer bisher Gravity Sync eingesetzt hat, sollte zeitnah migrieren – Pi-hole v6 hat die interne Datenbankstruktur geändert, und Gravity Sync kommt damit nicht mehr klar.

Ein Punkt, den man im Hinterkopf behalten sollte: Nebula Sync synchronisiert in einem Intervall, nicht in Echtzeit. Änderungen an Blocklisten oder DNS-Einträgen auf dem Primary brauchen bis zu 5 Minuten, um auf dem Secondary anzukommen. Für ein Homelab ist das absolut akzeptabel.

Das nächste sinnvolle Upgrade wäre, die Pi-hole-Instanzen in LXC-Containern auf Proxmox zu betreiben und Keepalived direkt auf den Containern zu konfigurieren – aber das ist ein eigener Artikel.

Quellen