Zum Inhalt springen
labkompass

Guides · Netzwerk

Pi-hole mit Docker Compose betreiben

Pi-hole als Container statt als Systeminstallation: eine nachvollziehbare compose.yaml, der Umgang mit belegtem Port 80, ein Speicherlimit für kleine Geräte und die Frage, welchen Upstream-DNS du eintragen solltest.

Von Niclas Hennrich · Veröffentlicht: · 4 Min. Lesezeit


Kurz gesagt: Lege ein Verzeichnis wie /opt/pihole an, schreib eine compose.yaml mit Port 53 für DNS, einem freien Host-Port für die Weboberfläche und einem Volume für /etc/pihole, starte mit docker compose up -d und trage die IP des Hosts anschließend als DNS-Server im Router ein. Kritisch sind nur zwei Dinge: Port 53 muss frei sein, und der Upstream-DNS ist eine bewusste Entscheidung.

Pi-hole filtert Werbung und Tracker für das ganze Netz, indem es als DNS-Server arbeitet und Anfragen an bekannte Werbedomains ins Leere laufen lässt. Es läuft auf jedem kleinen Dauerläufer — einem Raspberry Pi, einem Mini-PC, einem NAS.

Dieser Guide beschreibt die Container-Variante. Sie lohnt vor allem dann, wenn auf dem Gerät noch etwas anderes läuft: Das gesamte Setup steht in einer Datei, die du versionieren und auf einem anderen Host identisch wieder hochziehen kannst.

Voraussetzungen und der Port-53-Check #

Nötig sind Docker samt Compose-Plugin sowie ein Host mit fester IP-Adresse — ein DNS-Server, dessen Adresse sich ändert, fällt für das ganze Netz aus. Vergib die Adresse am besten als feste Zuordnung im Router, nicht statisch auf dem Gerät.

Vor dem Start die wichtigste Prüfung: Ist Port 53 frei?

sudo ss -tulpn | grep ':53'

Kommt keine Ausgabe, ist alles gut. Erscheint systemd-resolved, belegt der lokale DNS-Stub den Port. Dann deaktivierst du diesen Stub:

sudo mkdir -p /etc/systemd/resolved.conf.d
printf '[Resolve]\nDNSStubListener=no\n' | \
  sudo tee /etc/systemd/resolved.conf.d/10-no-stub.conf
sudo systemctl restart systemd-resolved

Danach ist Port 53 frei für Pi-hole.

Die compose.yaml #

Lege ein Verzeichnis an und erstelle darin die Compose-Datei:

sudo mkdir -p /opt/pihole/etc-pihole
cd /opt/pihole
sudo nano compose.yaml
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    # Auf Geräten mit wenig RAM (z. B. Pi 3B+ mit 1 GB) sinnvoll
    mem_limit: 256m
    volumes:
      - /opt/pihole/etc-pihole:/etc/pihole
    environment:
      TZ: "Europe/Berlin"
      FTLCONF_webserver_api_PASSWORD: "<PASSWORT>"
      # Upstream-DNS — bewusste Entscheidung, siehe unten
      FTLCONF_dns_upstreams: "<ROUTER-IP>"

Drei Stellen verdienen einen zweiten Blick:

Das Port-Mapping 8080:80 legt die Weboberfläche auf Host-Port 8080. Das ist der einfachste Weg, Pi-hole neben einem bereits laufenden Webserver zu betreiben — hier stammt die Wahl daraus, dass Port 80 auf dem Gerät schon von einem nginx belegt war. Ist bei dir Port 80 frei, kannst du auch 80:80 schreiben. Die DNS-Ports bleiben in jedem Fall auf 53.

Das Speicherlimit ist optional, auf kleinen Geräten aber hilfreich: Es verhindert, dass ein einzelner Container im Fehlerfall den gesamten Arbeitsspeicher belegt. 256 MB reichen für Pi-hole im Heimnetz.

Das Volume bindet /opt/pihole/etc-pihole in den Container. Dort liegen Blocklisten, Einstellungen und Statistiken — also alles, was ein Update überleben soll. Dieses Verzeichnis gehört ins Backup; in ein Git-Repository gehört es dagegen nicht, weil es Laufzeitdaten und die Query-Datenbank enthält.

Starten:

sudo docker compose up -d
sudo docker compose logs -f pihole

Die Weboberfläche erreichst du danach unter http://<HOST-IP>:8080/admin.

Die eigentliche Entscheidung: Welcher Upstream? #

Pi-hole beantwortet keine DNS-Anfragen selbst, sondern entscheidet nur, ob eine Domain blockiert wird. Alles andere reicht es an einen Upstream-DNS weiter — und dessen Wahl bestimmt, wer die Anfragen deines Netzes am Ende sieht.

Router als Upstream (im Beispiel oben): Lokale Gerätenamen, die dein Router kennt, bleiben auflösbar — praktisch, wenn du Geräte im Heimnetz unter Namen ansprichst. Der Preis: Router und Provider sehen weiterhin jede Anfrage. Für den Einstieg ist das der bequemste Weg, und genau so lief dieses Setup zunächst.

Eigener rekursiver Resolver (etwa Unbound): Dein Netz fragt die DNS-Hierarchie selbst ab, statt einem Dritten zu vertrauen. Dafür brauchst du eigene Einträge für lokale Namen — sonst laufen interne Hostnamen ins Leere oder, schlimmer, nach außen. Wie das sauber zusammenspielt, steht im Guide Pi-hole mit Unbound einrichten.

Die Reihenfolge, die sich bewährt: erst mit dem Router als Upstream starten und prüfen, ob das Blocken im Alltag funktioniert — dann auf einen eigenen Resolver umstellen, wenn du die Namensauflösung im Griff hast.

Pi-hole im Netz aktivieren #

Damit das Netz den Filter auch nutzt, muss der Router Pi-hole als DNS-Server verteilen. Trage die IP des Docker-Hosts als DNS-Server in den DHCP-Einstellungen deines Routers ein.

Ein Detail, das gern Zeit kostet: Viele Router verteilen den DNS-Server nur beim Erneuern der DHCP-Lease. Geräte, die schon eine Adresse haben, nutzen noch den alten Eintrag. Ein Neustart der Geräte oder das Ablaufen der Lease sorgt für Klarheit. Ob es funktioniert, siehst du im Pi-hole-Dashboard: Dort tauchen die anfragenden Clients einzeln auf.

Updates und Rückbau #

Ein Update ist ein Image-Wechsel:

cd /opt/pihole
sudo docker compose pull
sudo docker compose up -d

Weil die Konfiguration im Volume liegt, bleiben Blocklisten und Einstellungen erhalten. Und falls Pi-hole doch nicht das Richtige ist:

sudo docker compose down

Danach im Router den DNS-Eintrag zurücksetzen — das Netz löst wieder über den Router auf, und auf dem System selbst bleibt nichts zurück außer dem Verzeichnis unter /opt/pihole.

Wie es weitergeht #

Wenn Pi-hole läuft, sind die beiden nächsten sinnvollen Schritte: Unbound als rekursiver Upstream, damit die Auflösung nicht mehr über Router und Provider läuft, und — falls du noch schwankst, ob Pi-hole überhaupt die richtige Wahl ist — der Vergleich Pi-hole gegen AdGuard Home.

Häufige Fragen

Warum Pi-hole als Container statt direkt installiert?
Weil Konfiguration und Zustand sauber getrennt bleiben: Die compose.yaml beschreibt das gesamte Setup in einer versionierbaren Datei, ein Update ist ein Image-Wechsel, und ein Rückbau hinterlässt keine Spuren im System. Auf einem Gerät, das noch andere Dienste trägt, ist das der übersichtlichere Weg. Wer ausschließlich Pi-hole betreibt und keine Container nutzt, fährt mit der klassischen Installation genauso gut.
Pi-hole startet nicht, weil Port 53 belegt ist. Was tun?
Auf vielen Systemen hält systemd-resolved den Port 53 besetzt. Prüfen lässt sich das mit sudo ss -tulpn und der Suche nach Port 53. Die saubere Lösung ist, den DNSStubListener von systemd-resolved zu deaktivieren, damit der Port frei wird. Erst danach kann Pi-hole selbst auf 53 lauschen.
Wie ändere ich den Web-Port, wenn Port 80 schon belegt ist?
Im Port-Mapping der compose.yaml: Die Angabe 8080:80 bedeutet, dass der Container-Port 80 auf dem Host unter 8080 erreichbar ist. Die Weboberfläche liegt dann unter http://<HOST-IP>:8080/admin. Der DNS-Port 53 muss dagegen unverändert bleiben, weil Clients DNS-Anfragen immer an Port 53 schicken.
Welchen Upstream-DNS soll ich in Pi-hole eintragen?
Trägst du deinen Router ein, bleiben lokale Namen des Routers auflösbar — der Router und darüber der Provider sehen aber weiterhin alle Anfragen. Trägst du einen eigenen rekursiven Resolver wie Unbound ein, fragt dein Netz die DNS-Hierarchie selbst ab, und du brauchst für lokale Namen eigene Einträge. Beides ist legitim; die Entscheidung hängt davon ab, ob dir lokale Namensauflösung oder Unabhängigkeit wichtiger ist.
Gehen meine Einstellungen bei einem Container-Update verloren?
Nein, sofern das Verzeichnis mit der Pi-hole-Konfiguration als Volume eingebunden ist. Der Container ist dann austauschbar, während Blocklisten, Einstellungen und Statistiken auf dem Host liegen. Genau dieses Verzeichnis gehört auch in dein Backup — der Container selbst nicht, der wird bei Bedarf einfach neu erzeugt.

Weiterlesen