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 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:latestzieht die neueste Stable-Version. Für reproduzierbare Deployments pinnst du besser ein datumsbasiertes Tag wiepihole/pihole:2026.05.0(aktuellster Stand Mitte 2026; davor2026.04.1,2026.04.0). Die Tags folgen keinem SemVer, sondern dem Release-Datum. Der Tagnightlyist der Entwicklungszweig und nichts für den Produktivbetrieb. - ports:
53/tcpund53/udpsind für DNS zwingend,80für den Web-Admin.443ist optional für die HTTPS-Web-UI. Wer DHCP oder NTP über Pi-hole betreiben will, ergänzt67:67/udpbzw.123:123/udp. - volumes:
./etc-pihole:/etc/piholeist 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.dist 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) undSYS_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 useauf Port 53:systemd-resolvedbelegt noch Port 53. SetzeDNSStubListener=no(Schritt 1) und startesystemd-resolvedneu. Mitsudo 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.confund startesystemd-resolvedneu. - Clients aus anderen Subnetzen werden ignoriert:
FTLCONF_dns_listeningModeaufALLsetzen – im Docker-Default-Bridge-Netz zwingend. - Passwort unbekannt: Wurde kein
FTLCONF_webserver_api_passwordgesetzt, steht das zufällige Passwort indocker logs pihole. Neu setzen mitdocker 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 duFTLCONF_*, vor allemFTLCONF_webserver_api_password. - Permission-Fehler auf Fedora/RHEL: Bei SELinux ein
:zan 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:
- Docker Compose Grundlagen: Stacks verstehen und betreiben
- Traefik als Docker-Reverse-Proxy mit HTTPS einrichten
- WireGuard-VPN für Homeoffice und Standortanbindung einrichten
- Weitere Anleitungen in der Kategorie Netzwerk
Quellen: