Der 35-Watt-Mitbewohner (Teil 6): Werbung? Kenne ich nicht.

In Teil 1 habe ich den Lenovo M920q als Compute-Knoten eingerichtet. Teil 2 drehte sich um die sichere Anbindung ans Internet. Jetzt wird’s Zeit, dass der kleine Homeserver auch was fürs tägliche Wohlbefinden tut: Werbung killen.

Das Ziel: Netzwerkweite Ruhe

Werbung im Internet nervt. Auf dem Handy, auf dem Smart-TV, überall. Die Lösung für jeden Homeserver-Besitzer ist klar: Ein netzwerkweiter DNS-Blocker. Lange Zeit war Pi-hole der Platzhirsch, aber ich habe mich für AdGuard Home entschieden. Es wirkt moderner, unterstützt DNS-over-HTTPS/TLS von Haus aus und die Oberfläche gefällt mir einfach besser.

Klingt einfach, oder? Container starten, fertig. Oh, wie naiv ich war.

Der Endgegner: Port 53

Der erste Versuch, den Container im host Netzwerk zu starten, scheiterte sofort. Der Grund: Port 53.

Dieser Port ist der Standard für DNS-Anfragen. Auf fast jedem modernen Linux-Server (wie meinem Ubuntu-Host) wird dieser Port bereits vom Systemdienst systemd-resolved belegt. Der Container konnte nicht starten.

Klar, ich hätte einfach systemd-resolved deaktivieren können. Aber tief im Host-System herumzudoktern widerspricht meiner Philosophie: Der Docker-Host soll so vanilla wie möglich bleiben. Also: Plan B.

Die elegante Lösung: Macvlan

Ein Macvlan-Netzwerk in Docker ist genial. Es erlaubt dem Container, sich wie ein echtes, physisches Gerät im Netzwerk zu verhalten. Er bekommt seine eigene MAC-Adresse und viel wichtiger seine eigene IP vom Router, völlig unabhängig vom Host-Server.

Das bedeutet: Kein Port-Konflikt mehr auf Port 53!

Da mein Netzwerk Dual-Stack-fähig ist, wollte ich es richtig machen. Der Container bekommt sowohl eine feste IPv4 (192.168.50.250) als auch eine feste IPv6 (eine stabile ULA-Adresse, beginnend mit fd...). Zukunftssicher.

Die nächste Baustelle: Traefik

Der DNS-Blocker lief jetzt, aber ich wollte Zugriff auf das Web-Dashboard über meine sichere Domain (adguard.dieck-labs.de) via Traefik Reverse Proxy. Keine IP-Adressen mehr eintippen wie ein Höhlenmensch.

Hier wurde es knifflig.

Problem 1: Netzwerk-Isolation

Damit Traefik den Container erreichen kann, müssen beide im selben internen Docker-Netzwerk sein (bei mir apps). Aber wenn AdGuard im Macvlan hängt, ist es isoliert vom Rest der Docker-Welt.

Die Lösung: Der Container muss Mitglied in beiden Netzwerken sein. Macvlan fürs Heimnetz, apps für Traefik.

Problem 2: Das 502-Bad-Gateway-Drama

Nach der Traefik-Integration: Fehlermeldungen. 502 Bad Gateway. Stundenlange Fehlersuche.

Des Rätsels Lösung: Wenn man AdGuard Home initial über die Macvlan-IP einrichtet, konfiguriert es sich so, dass es nur auf dieser spezifischen IP lauscht. Anfragen von Traefik aus dem internen apps-Netzwerk? Werden ignoriert.

Der Fix: In der AdGuardHome.yaml musste ich die Binding-Adresse auf 0.0.0.0 setzen (alle Interfaces) und das Web-UI auf einen unüblichen Port wie 8087 legen. So kann Traefik sauber zugreifen, während DNS weiterhin auf Port 53 läuft.

Die Architektur

So sieht der Datenfluss jetzt aus:

Die config: Zwei Netzwerke, ein Container

Und hier ist das finale Docker Compose File (adguard.yml im Repo). Das Resultat von zu vielen Abenden vor dem Terminal:

version: "3.9"

services:
  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    restart: unless-stopped
    networks:
      # Netzwerk 1: Für DNS-Anfragen aus dem Heimnetz
      macvlan_net:
        ipv4_address: 192.168.50.250
        ipv6_address: fd15:d91c:273f:0::250
      # Netzwerk 2: Für die Kommunikation mit Traefik
      apps:
    volumes:
      # Docker verwaltet die Arbeitsdaten (Logs etc.)
      - adguard_work:/opt/adguardhome/work
      # WICHTIG: Lokaler Bind-Mount für die Config
      - ./adguard:/opt/adguardhome/conf
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=apps"
      - "traefik.http.routers.adguard.rule=Host(`adguard.dieck-labs.de`)"
      - "traefik.http.routers.adguard.entrypoints=websecure"
      - "traefik.http.routers.adguard.tls.certresolver=myresolver"
      # Traefik kommuniziert intern über diesen Custom-Port
      - "traefik.http.services.adguard.loadbalancer.server.port=8087"

networks:
  macvlan_net:
    driver: macvlan
    enable_ipv6: true
    driver_opts:
      parent: ens18 # Das physische Interface des Servers
    ipam:
      config:
        - subnet: 192.168.50.0/24
          gateway: 192.168.50.1
        - subnet: fd15:d91c:273f:0::/64
          gateway: fd15:d91c:273f:0::1
  apps:
    external: true # Das existierende Traefik-Netzwerk

volumes:
  adguard_work:
    external: true

Der Config-Trick

Ein wichtiges Detail: Die AdGuardHome.yaml im ./adguard Ordner muss manuell angepasst werden:

http:
  address: 0.0.0.0:8087  # Auf allen Interfaces lauschen
dns:
  bind_hosts:
    - 192.168.50.250
    - "fd15:d91c:273f:0::250"
  port: 53

Ich habe zuerst versucht, die Config über Docker configs einzubinden sehr elegant, “Infrastructure as Code” und so. Aber: Read-only. Das Dashboard konnte keine Änderungen mehr speichern. Der gute alte Bind-Mount war am Ende die pragmatischere Lösung.

Die Fritz!Box füttern

Ein netzwerkweiter Adblocker bringt nichts, wenn niemand ihn nutzt. Anstatt an jedem Gerät manuell die DNS-Einstellungen zu ändern, bringen wir der Fritz!Box bei, AdGuard als Standard-DNS an alle Geräte zu verteilen.

  1. Fritz!Box öffnen: http://fritz.boxInternetZugangsdatenDNS-Server
  2. DNSv4 eintragen: “Andere DNSv4-Server verwenden” aktivieren und 192.168.50.250 als bevorzugten DNS-Server eintragen
  3. DNSv6 eintragen: Falls gewünscht, auch die IPv6-Adresse (fd15:d91c:273f:0::250) für DNSv6 hinterlegen
  4. Geräte reconnecten: Am einfachsten WLAN kurz aus- und einschalten, damit die neuen DNS-Einstellungen gezogen werden

DNS-Einstellungen in der Fritz!Box

Das Ergebnis

Wenn ich jetzt im AdGuard-Dashboard auf das Anfragenprotokoll klicke, sehe ich live, wie Tracking-Domains ins digitale Nirvana geschickt werden.

AdGuard Dashboard Die YouTube-App auf dem Smart-TV lädt schneller (weniger Werbe-Requests), und das Gewissen ist auch beruhigt weniger Tracking bedeutet mehr Privatsphäre für alle im Haushalt.

Was ich gelernt habe:

  • Macvlan ist die elegante Lösung für Port-Konflikte auf dem Host
  • Zwei-Netzwerk-Setups in Docker erfordern sorgfältige Binding-Konfiguration
  • Manchmal ist ein pragmatischer Bind-Mount besser als eine “saubere” read-only Config

Der kleine 35-Watt-Mitbewohner hat jetzt eine weitere Aufgabe: Türsteher fürs Heimnetz. In Teil 4 wird’s dann um Monitoring gehen denn was bringt ein Homelab, wenn man nicht ständig auf bunte Grafana-Dashboards starren kann?