Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Server & Netzwerk 02.06.2026 · 11 min Lesezeit

Pi-hole mit Docker: netzwerkweiter Werbe- und Tracker-Blocker

Pi-hole per Docker Compose betreiben und Werbung sowie Tracker netzwerkweit über DNS blockieren. Schritt für Schritt mit fertiger compose.yaml, Lösung des Port-53-Konflikts, Web-Admin, Blocklisten und Backup.

Pi-hole als netzwerkweiter Werbe- und Tracker-Blocker per Docker auf einem Linux-Server

Pi-hole ist ein DNS-basierter, netzwerkweiter Werbe- und Tracker-Blocker. Statt in jedem Browser ein Plugin zu installieren, fängt Pi-hole unerwünschte Domains direkt auf DNS-Ebene ab – und schützt damit alle Geräte im Netz: PCs, Smartphones, Smart-TVs und IoT-Hardware. Du betreibst Pi-hole als zentralen DNS-Server, zeigst Router und Clients darauf, und Anfragen an bekannte Werbe-, Tracking- und Malware-Domains werden gar nicht erst beantwortet. Diese Anleitung zeigt dir, wie du Pi-hole auf einem Linux-Server (Debian 12 oder Ubuntu 24.04) per Docker Compose aufsetzt – inklusive der typischen Stolperfalle mit systemd-resolved auf Port 53, sauberer Persistenz über Volumes, Web-Admin, Blocklisten-Pflege, lokalen DNS-Einträgen sowie Update- und Backup-Workflow. Zielgruppe sind Admins im Mittelstand und ambitionierte Heimserver-Nutzer.

Voraussetzungen

Pi-hole ist extrem leichtgewichtig und läuft sogar auf einem Raspberry Pi. Es ist ein reines CPU-Workload – eine GPU ist weder nötig noch relevant. Du brauchst nur einen kleinen, dauerhaft laufenden Server im LAN.

  • Server: Linux mit Docker und Docker Compose (Debian 12 oder Ubuntu 24.04). Mehr brauchst du an Software nicht.
  • RAM: rund 512 MB genügen. Mehr RAM lohnt nur für sehr große Query-Logs.
  • CPU: beliebig, keine GPU erforderlich. Ein einzelner Kern reicht aus.
  • Storage: wenige hundert MB. Mehr nur, wenn du lange Query-Historien speichern willst.
  • Netzwerk: idealerweise eine feste IP-Adresse für den Server, denn Router und Clients zeigen später dauerhaft auf diese Adresse als DNS-Server.
  • Optional: eine Domain plus Reverse-Proxy und HTTPS, falls du den Web-Admin abgesichert erreichbar machen willst.

Falls Docker auf dem Server noch fehlt oder du mit dem Compose-Konzept noch nicht vertraut bist, lies vorab unsere Grundlagen zu Docker Compose und Stacks.

Schritt 1: Port 53 freigeben (systemd-resolved)

Das ist die wichtigste Vorarbeit – und die häufigste Fehlerquelle. Auf Debian 12 und Ubuntu 24.04 belegt systemd-resolved standardmäßig den DNS-Port 53. Startet Pi-hole, scheitert der Container mit address already in use. Du musst den Stub-Listener deaktivieren bevor du den Container startest.

Lege dazu eine Drop-in-Konfiguration an, die den Stub-Listener abschaltet:

sudo sh -c 'mkdir -p /etc/systemd/resolved.conf.d && printf "[Resolve]\nDNSStubListener=no\n" | tee /etc/systemd/resolved.conf.d/no-stub.conf'

Damit der Host danach selbst noch Namen auflösen kann, biegst du /etc/resolv.conf auf den echten resolved-Resolver um und startest den Dienst neu. Diesen Schritt nicht vergessen – sonst kann der Server selbst keine Upstream-Namen mehr auflösen:

sudo sh -c 'rm -f /etc/resolv.conf && ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf'
sudo systemctl restart systemd-resolved

Prüfe anschließend, dass Port 53 wirklich frei ist. Die folgende Ausgabe sollte keine Zeile mehr für Port 53 zeigen:

sudo ss -tulpn | grep ':53'

Schritt 2: Verzeichnis und compose.yaml anlegen

Lege ein Projektverzeichnis an und wechsle hinein. Die Volumes legt Docker später relativ zu diesem Ordner an:

mkdir -p /opt/pihole
cd /opt/pihole

Die folgende compose.yaml entspricht der offiziellen Pi-hole-Vorlage und ist copy-paste-fertig. Du musst nur das Passwort und gegebenenfalls die Zeitzone anpassen:

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest   # alternativ fixes Tag, z. B. pihole/pihole:2026.05.0
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
      - "443:443/tcp"          # optional, HTTPS Web-UI
    environment:
      TZ: 'Europe/Berlin'
      FTLCONF_webserver_api_password: 'BITTE-AENDERN'
      FTLCONF_dns_listeningMode: 'ALL'
    volumes:
      - './etc-pihole:/etc/pihole'
    cap_add:
      - NET_ADMIN
      - SYS_TIME
      - SYS_NICE
    restart: unless-stopped

Die wichtigsten Punkte zum Verständnis:

  • image: pihole/pihole:latest zieht die neueste Stable-Version. Für reproduzierbare Deployments pinnst du besser ein datumsbasiertes Tag wie pihole/pihole:2026.05.0 (aktuellster Stand Mitte 2026; davor 2026.04.1, 2026.04.0). Die Tags folgen keinem SemVer, sondern dem Release-Datum. Der Tag nightly ist der Entwicklungszweig und nichts für den Produktivbetrieb.
  • ports: 53/tcp und 53/udp sind für DNS zwingend, 80 für den Web-Admin. 443 ist optional für die HTTPS-Web-UI. Wer DHCP oder NTP über Pi-hole betreiben will, ergänzt 67:67/udp bzw. 123:123/udp.
  • volumes: ./etc-pihole:/etc/pihole ist die zentrale Persistenz – hier liegen Konfiguration, Datenbanken und Adlists. Ohne dieses Volume gehen Passwort, Einstellungen und Blocklisten bei jeder Container-Neuerstellung verloren. Das früher übliche ./etc-dnsmasq.d ist in Pi-hole v6 nicht mehr nötig.
  • FTLCONF_webserver_api_password: setzt das Admin-Passwort (Pi-hole v6). Lässt du die Variable weg, vergibt FTL ein zufälliges Passwort, das du dann aus den Logs lesen müsstest.
  • FTLCONF_dns_listeningMode: 'ALL': im Docker-Default-Bridge-Netz zwingend, sonst beantwortet Pi-hole keine Anfragen aus anderen Subnetzen und ignoriert deine Clients.
  • cap_add: NET_ADMIN (für den DHCP-Server), SYS_TIME (NTP-Client) und SYS_NICE (CPU-Priorität). Wichtig: Seit Release 2022.04 nutzt du nicht mehr --privileged, sondern nur diese expliziten Capabilities.

Schritt 3: Container starten und Passwort prüfen

Starte den Stack im Hintergrund:

docker compose up -d

Beim ersten Start initialisiert Pi-hole die Datenbanken und lädt die Standard-Blockliste (StevenBlack-Hosts). Den Fortschritt und einen eventuell generierten Status siehst du in den Logs:

docker compose logs -f pihole
docker compose ps

Falls du FTLCONF_webserver_api_password bewusst nicht gesetzt hast, vergibt FTL ein zufälliges Passwort und schreibt es in die Logs. Du kannst es jederzeit neu setzen, ohne den Container neu zu erstellen:

docker exec -it pihole pihole setpassword 'NEUES-PASSWORT'

Schritt 4: Web-Admin aufrufen und einrichten

Ruf den Web-Admin im Browser auf. Über Port 80 erreichst du ihn unter http://SERVER-IP/admin, mit aktiviertem Port 443 alternativ unter https://SERVER-IP/admin. Melde dich mit dem Passwort an, das du in FTLCONF_webserver_api_password gesetzt hast.

Im Dashboard siehst du sofort die Live-Statistik: Gesamtzahl der DNS-Anfragen, Anteil der geblockten Domains und die aktivsten Clients. Solange noch keine Clients auf Pi-hole zeigen, bleibt die Statistik leer – das ist normal und ändert sich in Schritt 6. Nimm dir kurz Zeit, um die Bereiche kennenzulernen:

  • Settings -> DNS: hier legst du die Upstream-Resolver fest (z. B. Quad9, Cloudflare oder dein eigener Resolver).
  • Lists: Verwaltung der Blocklisten (Adlists).
  • Settings -> Local DNS Records / Local CNAME Records: eigene Namen im LAN auflösen.
  • Query Log: live nachvollziehen, welche Domains angefragt und geblockt werden.

Schritt 5: Blocklisten und lokale DNS-Einträge pflegen

Pi-hole bringt von Haus aus die StevenBlack-Hosts-Liste mit. Weitere Listen fügst du im Web-Admin unter Lists hinzu: Adlist-URL eintragen, speichern und anschließend die Gravity-Datenbank neu aufbauen. Das geht per Button Update Gravity in der UI oder auf der Kommandozeile:

docker exec -it pihole pihole -g

Die Adlist-Updates laufen ohnehin automatisch wöchentlich per Cron im Container – ein manueller Lauf ist nur nötig, wenn du gerade eine neue Liste ergänzt hast. Übertreib es nicht mit zu vielen Listen: Qualität schlägt Quantität, sonst riskierst du Fehlblockaden (false positives) auf legitimen Seiten.

Sehr praktisch sind lokale DNS-Einträge. Damit löst du eigene Hostnamen im LAN auf, ohne einen separaten DNS-Server zu betreiben. In Pi-hole v6 findest du das unter Settings -> Local DNS Records (A-Records, z. B. nas.heimnetz.lan auf eine interne IP) und Local CNAME Records (Aliase auf bestehende Namen). So zeigst du etwa interne Dienste hinter sprechende Namen, statt dir IP-Adressen zu merken.

Schritt 6: Router und Clients auf Pi-hole zeigen

Jetzt kommt der entscheidende Schritt, ohne den gar nichts geblockt wird: Deine Geräte müssen Pi-hole aktiv als DNS-Server nutzen. Am elegantesten verteilst du die Pi-hole-IP zentral über den Router.

  • Empfohlen – über den Router: Trage im DHCP-Bereich deines Routers die Pi-hole-IP als DNS-Server ein (DHCP-Option). Dann beziehen alle Clients diese Adresse automatisch. Bei manchen Routern (z. B. FRITZ!Box) hinterlegst du den DNS-Server in den Internet-/DNS-Einstellungen.
  • Alternativ – pro Gerät: Setze auf einzelnen Clients manuell die Pi-hole-IP als bevorzugten DNS-Server.

Trage als bevorzugten DNS-Server nur die Pi-hole-IP ein. Ein zweiter, öffentlicher DNS-Server als Fallback würde dazu führen, dass Clients ihn nutzen und Pi-hole umgehen – dann wird sporadisch nichts geblockt. Beachte außerdem: Geräte mit fest verdrahtetem DNS oder Browser mit aktivem DNS-over-HTTPS (DoH) umgehen Pi-hole. Solche Ausnahmen musst du gezielt unterbinden, wenn du lückenlos blockieren willst.

Nach der Umstellung siehst du im Dashboard zügig die ersten Anfragen und geblockten Domains. Ein schneller Funktionstest von einem Client aus:

nslookup doubleclick.net SERVER-IP

Pi-hole sollte für eine geblockte Domain 0.0.0.0 zurückgeben.

Schritt 7: Reverse-Proxy und HTTPS (Sicherheit)

Der Web-Admin auf Port 80 gehört nicht ungeschützt ins Internet, und Pi-hole darf kein offener Resolver sein, der DNS-Anfragen aus dem Internet beantwortet. Betreibe Pi-hole nur im LAN. Willst du den Web-Admin extern erreichen, stellst du einen Reverse-Proxy mit TLS und Authentifizierung davor – den DNS-Dienst auf Port 53 exponierst du dabei niemals nach außen.

Eine saubere Variante ist Traefik als Reverse-Proxy mit automatischem Let's-Encrypt-Zertifikat. Wie du das aufsetzt, beschreibt unsere Anleitung Traefik als Docker-Reverse-Proxy mit HTTPS einrichten. Für reinen Fernzugriff auf den Admin ist ein VPN oft die einfachere und sicherere Lösung – siehe WireGuard-VPN für Homeoffice und Standortanbindung einrichten. Dann bleibt der Web-Admin komplett im internen Netz und ist trotzdem von unterwegs erreichbar.

Schritt 8: Update-Workflow

Updates sind unkompliziert: neues Image ziehen, Container neu erstellen, optional alte Images aufräumen. Weil die Konfiguration im Volume ./etc-pihole liegt, bleibt alles erhalten:

docker compose pull && docker compose up -d
docker image prune -f   # optional, alte Images aufraeumen

Hinweis zu automatischen Updates: Watchtower kann bei Pi-hole Probleme verursachen. Bleib lieber beim kontrollierten docker compose pull && up -d, idealerweise mit gepinntem Versions-Tag – dann weißt du genau, welche Version läuft, und kannst vor einem Sprung das Changelog lesen.

Schritt 9: Backup

Der gesamte Zustand von Pi-hole – Konfiguration, Adlists und die Query-Datenbank – steckt im Volume ./etc-pihole. Für ein konsistentes Backup stoppst du den Container kurz, packst das Verzeichnis und startest wieder:

docker compose down && tar czf pihole-backup-$(date +%F).tar.gz ./etc-pihole && docker compose up -d

Alternativ nutzt du den eingebauten Teleporter im Web-Admin unter Settings -> Teleporter -> Export. Er erzeugt ein portables Archiv mit allen Einstellungen, das du auf einer frischen Pi-hole-Instanz wieder einspielen kannst – ideal für Migration oder Wiederherstellung. Sichere die Backups anschließend regelmäßig auf ein externes Ziel.

Troubleshooting

  • Container startet nicht, address already in use auf Port 53: systemd-resolved belegt noch Port 53. Setze DNSStubListener=no (Schritt 1) und starte systemd-resolved neu. Mit sudo ss -tulpn | grep ':53' prüfen, dass der Port frei ist.
  • Host kann nach Schritt 1 keine Namen mehr auflösen: Du hast die resolv.conf-Symlink-Korrektur vergessen. Setze den Symlink auf /run/systemd/resolve/resolv.conf und starte systemd-resolved neu.
  • Clients aus anderen Subnetzen werden ignoriert: FTLCONF_dns_listeningMode auf ALL setzen – im Docker-Default-Bridge-Netz zwingend.
  • Passwort unbekannt: Wurde kein FTLCONF_webserver_api_password gesetzt, steht das zufällige Passwort in docker logs pihole. Neu setzen mit docker exec -it pihole pihole setpassword 'NEUES-PASSWORT'.
  • Es wird gar nichts geblockt: Router/Clients zeigen nicht auf Pi-hole, oder ein zweiter öffentlicher DNS-Server dient als Fallback. Nur die Pi-hole-IP als DNS eintragen. Browser-DoH deaktivieren.
  • Alte Anleitung mit WEBPASSWORD / ServerIP / DNS1: Das sind v5-Variablen. In Pi-hole v6 nutzt du FTLCONF_*, vor allem FTLCONF_webserver_api_password.
  • Permission-Fehler auf Fedora/RHEL: Bei SELinux ein :z an den Volume-Mount anhängen, also ./etc-pihole:/etc/pihole:z.

Häufige Fragen

Brauche ich eine GPU oder starke Hardware für Pi-hole?

Nein. Pi-hole ist ein reines CPU-Workload und braucht keine GPU. Es ist so leichtgewichtig, dass es auf einem Raspberry Pi läuft. Rund 512 MB RAM und wenige hundert MB Storage genügen; mehr lohnt nur für sehr große Query-Logs.

Warum scheitert der Start mit „address already in use“?

Weil systemd-resolved auf Debian 12 und Ubuntu 24.04 standardmäßig Port 53 belegt. Du musst vor dem Container-Start den Stub-Listener per DNSStubListener=no deaktivieren, systemd-resolved neu starten und die resolv.conf auf den echten Resolver umbiegen.

Was ist der Unterschied zwischen FTLCONF_webserver_api_password und WEBPASSWORD?

WEBPASSWORD ist das alte v5-Verfahren. In Pi-hole v6 setzt du das Admin-Passwort über FTLCONF_webserver_api_password. Generische FTLCONF_<setting>-Variablen überschreiben die pihole.toml. Lässt du die Passwort-Variable weg, vergibt FTL ein zufälliges Passwort.

Wie sichere ich Pi-hole, wenn ich von außen zugreifen will?

Stelle den Web-Admin niemals ungeschützt ins Internet und betreibe Pi-hole nicht als offenen Resolver. Für Fernzugriff nutzt du entweder ein VPN wie WireGuard oder einen Reverse-Proxy mit TLS und Authentifizierung. Der DNS-Port 53 bleibt ausschließlich im LAN erreichbar.

Wie aktualisiere ich Pi-hole und meine Blocklisten?

Pi-hole aktualisierst du mit docker compose pull && docker compose up -d. Die Blocklisten (Gravity) werden automatisch wöchentlich per Cron aktualisiert; manuell stößt du das mit docker exec -it pihole pihole -g oder dem Button Update Gravity in der Web-UI an.

Was ist der Unterschied zu AdGuard Home?

AdGuard Home ist eine funktional ähnliche Alternative mit eigenem Docker-Image (adguard/adguardhome) und eigener Web-UI. Es bringt verschlüsseltes DNS (DoH/DoT) von Haus aus mit und richtet sich oft per Setup-Assistent ein. Pi-hole ist dafür extrem ausgereift, sehr gut dokumentiert und bietet mit Adlists, lokalen DNS-/CNAME-Records und Teleporter alles, was du im Mittelstand brauchst.

Fazit

Mit Docker Compose hast du Pi-hole in rund einer halben Stunde als netzwerkweiten Werbe- und Tracker-Blocker am Laufen. Die entscheidenden Stellschrauben sind die Freigabe von Port 53 gegenüber systemd-resolved, die korrekten FTLCONF_*-Variablen (Passwort und listeningMode: ALL) sowie das persistente Volume ./etc-pihole. Sobald Router und Clients auf die Pi-hole-IP als DNS zeigen, profitiert jedes Gerät im Netz – ganz ohne Plugins. Halte die Blocklisten schlank, exponiere den Dienst nicht ins Internet und mach vor Updates ein Backup oder einen Teleporter-Export. Dann läuft dein zentraler DNS-Filter stabil und wartungsarm. Wer lieber DoH/DoT von Haus aus möchte, findet in AdGuard Home eine gleichwertige Alternative.

Weiterführende Anleitungen und Quellen

Weiterführende Anleitungen aus unserem Wissensbereich:

Quellen: