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

Eigener DNS für unterwegs: AdGuard Home mit DoH, DoT und DoQ auf dem VPS

Teil 9 der Reihe Server im Netz: AdGuard Home auf dem VPS als verschlüsselter DNS-Server für unterwegs. Mit Wildcard-Zertifikat per lego, Client-IDs gegen offene Resolver, Caddy für DoH und zwei Fallen aus dem Test.

Geprüft am 05.11.2026 · für AdGuard Home 0.107.80, lego 5.6.0, Caddy 2.11.7

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Symbolbild: Server mit leuchtendem Kabel zur Weltkarte, Titel Eigener DNS für unterwegs

Im Heimnetz filtert vielleicht schon ein Pi-hole oder AdGuard Home Werbung und Tracker. Unterwegs im Mobilfunk oder im Hotel-WLAN ist der Schutz weg, und die DNS-Anfragen laufen unverschlüsselt über fremde Server. Ein eigener DNS-Server auf dem VPS ändert das: Er nimmt nur verschlüsselte Anfragen über DNS-over-TLS (DoT), DNS-over-HTTPS (DoH) und DNS-over-QUIC (DoQ) an, filtert wie zu Hause und beantwortet nur Geräte, die er kennt. Diese Anleitung richtet AdGuard Home dafür ein, holt per DNS-01 ein Wildcard-Zertifikat für die Geräte-Kennungen und zeigt zwei Fallen aus dem Test: eine, bei der der eigene Name während der Zertifikatsprüfung verschwindet, und eine, bei der AdGuard Home gar nicht erst startet.

Dies ist Teil 9 der Reihe „Server im Netz“. Als Beispiel dient ein netcup-VPS mit Ubuntu und Docker, auf dem Caddy die Domain sedv-lab.de mit HTTPS ausliefert. Der DNS-Server bekommt den Namen dns.sedv-lab.de.

Voraussetzungen

  • Der VPS aus Teil 4 mit Caddy im Host-Netz und Docker
  • Eine Domain, deren DNS-Zone Sie über eine API ändern können, im Beispiel das klassische DNS von netcup mit API-Schlüssel aus dem Kundenkontrollpanel
  • Freie Ports 853/TCP und 853/UDP auf dem VPS
  • Ein Gerät zum Testen, etwa ein iPhone, oder ein Linux-Rechner mit kdig aus dem Paket knot-dnsutils

So erkennt AdGuard Home Ihre Geräte

Verschlüsseltes DNS hat einen Vorteil gegenüber klassischem DNS: Das Gerät kann sich ausweisen. AdGuard Home nennt das Client-ID. Bei DoH steht sie im Pfad, etwa https://dns.sedv-lab.de/dns-query/iphone. Bei DoT und DoQ steht sie im Servernamen, den das Gerät beim TLS-Aufbau schickt, etwa iphone.dns.sedv-lab.de. Trägt man die Client-IDs in die Liste der zugelassenen Clients ein, beantwortet AdGuard Home nur noch diese Geräte. So wird aus dem DNS-Server kein offener Resolver, den jeder nutzen oder für Angriffe missbrauchen kann.

Für DoT und DoQ braucht der Server damit ein Zertifikat, das für jeden Namen unterhalb von dns.sedv-lab.de gilt, also ein Wildcard-Zertifikat für *.dns.sedv-lab.de. Das stellt Let's Encrypt nur über die DNS-Prüfung DNS-01 aus, bei der ein TXT-Eintrag in der Zone gesetzt wird. Caddy kann das nur mit einem Zusatzmodul. Wir nehmen deshalb lego, ein eigenständiges ACME-Programm mit Unterstützung für viele DNS-Anbieter.

Schritt 1: Feste DNS-Einträge anlegen

Im Beispiel zeigt bereits ein Wildcard-Eintrag * auf den Server, dns.sedv-lab.de und iphone.dns.sedv-lab.de lösen also schon auf. Darauf sollten Sie sich hier nicht verlassen. Legen Sie in der Zone feste Einträge an:

dns      A     203.0.113.10
dns      AAAA  2001:db8:1:2::10
*.dns    A     203.0.113.10
*.dns    AAAA  2001:db8:1:2::10

Der Grund zeigte sich im Test. Wir hatten lego zuerst ohne diese Einträge gestartet und dabei alle 20 Sekunden beim Nameserver von netcup nachgefragt. Sobald der TXT-Eintrag _acme-challenge.dns.sedv-lab.de sichtbar war, lieferte der Nameserver für dns.sedv-lab.de und für jeden Namen darunter keine Adresse mehr. Das ist kein Fehler von netcup, sondern in RFC 4592 so geregelt: Ein Wildcard gilt nur für Namen, die in der Zone gar nicht vorkommen, und durch den TXT-Eintrag existiert dns plötzlich als Zwischenstufe. Für Namen darunter wie iphone.dns greift *.sedv-lab.de dann ebenfalls nicht mehr, dort müsste ein eigener Eintrag *.dns stehen. Im Test war der Name so rund 10 Minuten lang nicht auflösbar, und das würde sich bei jeder Erneuerung wiederholen. Mit festen Einträgen für dns und *.dns greift die Regel nach RFC 4592 nicht.

Verifizieren: dig +short iphone.dns.sedv-lab.de A @root-dns.netcup.net liefert die IP des Servers. Bei netcup dauerte es im Test 7 bis 9 Minuten, bis neue Einträge dort sichtbar waren.

Schritt 2: Wildcard-Zertifikat mit lego

Die Zugangsdaten für die DNS-API kommen in eine Datei, die nur root lesen darf:

mkdir -p /opt/adguardhome/lego && cd /opt/adguardhome
cat > lego/netcup.env <<'EOF'
NETCUP_CUSTOMER_NUMBER=12345
NETCUP_API_KEY=IHR-API-KEY
NETCUP_API_PASSWORD=IHR-API-PASSWORT
NETCUP_PROPAGATION_TIMEOUT=1800
NETCUP_POLLING_INTERVAL=30
EOF
chmod 600 lego/netcup.env

NETCUP_PROPAGATION_TIMEOUT ist wichtig. lego wartet standardmäßig höchstens 900 Sekunden darauf, dass der TXT-Eintrag bei den Nameservern ankommt. Im Test lagen zwischen dem Anlegen und der Sichtbarkeit beim Nameserver von netcup knapp 9 Minuten. Mit dem Standardwert wäre das knapp geworden, 1800 Sekunden geben Luft.

docker run --rm -v "$PWD/lego:/data" --env-file lego/netcup.env goacme/lego:v5.6.0 \
  run --path /data --accept-tos --email admin@sedv-lab.de --dns netcup \
  -d dns.sedv-lab.de -d '*.dns.sedv-lab.de'

lego meldet beim Start this implementation use the legacy API, please use 'netcupv1' instead for Netcup CloudDNS API. Für das klassische DNS im Kundenkontrollpanel ist der Anbieter netcup richtig, netcupv1 gilt nur für das neuere CloudDNS. Dann folgt alle 30 Sekunden waiting for record propagation, bis Let's Encrypt prüfen kann.

Verifizieren: Am Ende stehen Server responded with a certificate und vier Dateien in lego/certificates/, darunter dns.sedv-lab.de.crt und dns.sedv-lab.de.key. Im Test dauerte der ganze Lauf 11 Minuten.

Schritt 3: AdGuard Home einrichten

Statt des Einrichtungsassistenten schreiben wir die Konfiguration selbst nach conf/AdGuardHome.yaml. Fehlende Einträge setzt AdGuard Home auf Standardwerte:

http:
  address: 127.0.0.1:3000
  doh:
    insecure_enabled: true
users:
  - name: admin
    password: BCRYPT-HASH
dns:
  bind_hosts:
    - 0.0.0.0
  port: 5300
  serve_plain_dns: false
  upstream_dns:
    - https://dns.quad9.net/dns-query
    - tls://dns.digitale-gesellschaft.ch
  bootstrap_dns:
    - 9.9.9.9
    - 149.112.112.112
  allowed_clients:
    - iphone
    - heimtest
  trusted_proxies:
    - 127.0.0.0/8
    - ::1/128
tls:
  enabled: true
  server_name: dns.sedv-lab.de
  port_https: 0
  port_dns_over_tls: 853
  port_dns_over_quic: 853
  certificate_path: /certs/certificates/dns.sedv-lab.de.crt
  private_key_path: /certs/certificates/dns.sedv-lab.de.key
filters:
  - enabled: true
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
    name: AdGuard DNS filter
    id: 1
schema_version: 34

Den BCrypt-Hash für das Passwort der Oberfläche erzeugen Sie laut Wiki von AdGuard Home mit htpasswd -B -C 10 -n -b admin 'IHR-PASSWORT' aus dem Paket apache2-utils und übernehmen den Teil nach dem Doppelpunkt. Die Oberfläche lauscht nur auf 127.0.0.1:3000, Sie öffnen sie über einen SSH-Tunnel. insecure_enabled erlaubt DoH über einfaches HTTP, denn HTTPS übernimmt Caddy. serve_plain_dns: false schaltet unverschlüsseltes DNS ab, der Port 5300 bleibt dann ganz zu.

Die compose.yaml:

services:
  adguardhome:
    image: adguard/adguardhome:v0.107.80
    container_name: adguardhome
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
      - ./lego:/certs:ro
docker compose up -d
docker logs adguardhome 2>&1 | grep -E "listening|fatal"
ss -tulpn | grep AdGuardHome

Hier lag die zweite Falle. Mit 0.0.0.0 und "::" unter bind_hosts, wie man es für IPv4 und IPv6 erwarten würde, brach AdGuard Home ab:

[fatal] starting dns server: configuring listeners: listening on tls addr [::]:853: listen tcp [::]:853: bind: address already in use

Für 0.0.0.0 öffnet AdGuard Home unter Linux einen gemeinsamen Socket für IPv4 und IPv6, das Log zeigt dafür [::]:853. Der zweite Eintrag will denselben Port noch einmal belegen. Lassen Sie "::" weg.

Verifizieren: ss zeigt *:853 für TCP und UDP und 127.0.0.1:3000, aber nichts auf 5300. Im Log steht plain dns is disabled. In der Oberfläche unter Einstellungen, Verschlüsselung erscheint das Zertifikat als gültig:

Verschlüsselungseinstellungen von AdGuard Home mit gültigem Wildcard-Zertifikat und DDR-Hinweis
Verschlüsselungseinstellungen von AdGuard Home: Die Zertifikatskette ist gültig (1), das Zertifikat gilt für *.dns.sedv-lab.de und dns.sedv-lab.de (2). Der Warnhinweis betrifft nur die automatische Erkennung über DDR (3).

Der Warnhinweis certificates has no IP addresses; DNS-over-TLS won't be advertised via DDR ist harmlos. DDR ist ein Verfahren, mit dem ein Gerät von einem klassischen DNS-Server erfährt, dass dieser auch verschlüsselt spricht. Dafür müsste die IP-Adresse im Zertifikat stehen. Let's Encrypt stellt solche Zertifikate nur mit sechs Tagen Laufzeit aus, für eingetragene Adressen wie hier spielt DDR keine Rolle.

Schritt 4: DoH über Caddy

Für DoH reicht Caddy nur die DoH-Pfade an AdGuard Home weiter, alles andere beantwortet es mit 404. So ist die Oberfläche auch versehentlich nicht von außen erreichbar:

dns.sedv-lab.de {
	import sicher
	@doh path /dns-query /dns-query/*
	handle @doh {
		reverse_proxy 127.0.0.1:3000
	}
	handle {
		respond 404
	}
}

Dank trusted_proxies sieht AdGuard Home trotzdem die echte Adresse des Geräts, die Caddy im Header mitschickt. Für dieses Zertifikat nutzt Caddy wie gewohnt die HTTP-Prüfung. Im Test schlug sie zuerst fehl mit no valid A records found for dns.sedv-lab.de, weil die neuen Einträge aus Schritt 1 noch nicht verteilt waren. Nach einem caddy reload einige Minuten später klappte es.

Verifizieren: curl -s -o /dev/null -w '%{http_code}\n' https://dns.sedv-lab.de/ liefert 404, die Abfrage unter /dns-query/heimtest liefert 200.

Schritt 5: Nur bekannte Geräte zulassen

Die Liste allowed_clients aus der Konfiguration erscheint in der Oberfläche unter Einstellungen, DNS-Einstellungen, Zugriffsrechte:

Zugriffsrechte in AdGuard Home mit den zugelassenen Client-IDs
Zugriffsrechte in AdGuard Home: Nur die eingetragenen Client-IDs dürfen den DNS-Server nutzen (1).

Getestet haben wir von einem Rechner im Heimnetz mit kdig:

kdig @dns.sedv-lab.de +tls +tls-hostname=heimtest.dns.sedv-lab.de +tls-sni=heimtest.dns.sedv-lab.de example.org A
kdig @dns.sedv-lab.de +quic +tls-hostname=heimtest.dns.sedv-lab.de +tls-sni=heimtest.dns.sedv-lab.de example.org A
kdig @dns.sedv-lab.de +https=/dns-query/heimtest +tls-hostname=dns.sedv-lab.de example.org A
AnfrageErgebnis im Test
DoT, DoQ und DoH mit zugelassener Client-IDstatus: NOERROR mit Antwort
DoT ohne Client-ID (dns.sedv-lab.de)status: REFUSED
DoT und DoH mit unbekannter Client-IDstatus: REFUSED, bei DoH mit HTTP-Status 200
Werbedomains doubleclick.net, googleadservices.comAntwort 0.0.0.0 aus der Sperrliste
Klassisches DNS auf Port 53 oder 5300 von außenkeine Verbindung

Solange einfaches DNS noch an war, bekamen fremde Absender über UDP auf Port 5300 überhaupt keine Antwort und über TCP REFUSED. AdGuard Home verwirft unbekannte UDP-Anfragen also, statt zu antworten. Das ist wichtig, denn genau diese Antworten nutzen Angreifer für Verstärkungsangriffe. Mit serve_plain_dns: false ist der Port zusätzlich zu.

Verifizieren: Im Abfrageprotokoll der Oberfläche steht bei den Anfragen die Client-ID.

Schritt 7: Zertifikat erneuern

Das Zertifikat gilt 90 Tage, lego erneuert es laut eigener Meldung frühestens nach rund 60 Tagen. Ein kleines Skript ruft lego auf und startet AdGuard Home neu, wenn sich das Zertifikat geändert hat:

#!/bin/sh
cd /opt/adguardhome
C=lego/certificates/dns.sedv-lab.de.crt
vorher=$(sha256sum "$C" | cut -d' ' -f1)
docker run --rm -v "$PWD/lego:/data" --env-file lego/netcup.env goacme/lego:v5.6.0 \
  run --path /data --accept-tos --email admin@sedv-lab.de --dns netcup \
  -d dns.sedv-lab.de -d '*.dns.sedv-lab.de' --no-random-sleep
nachher=$(sha256sum "$C" | cut -d' ' -f1)
[ "$vorher" != "$nachher" ] && docker restart adguardhome

Gespeichert als /opt/adguardhome/erneuern.sh mit chmod 700, dazu ein Eintrag in /etc/cron.d/adguard-zertifikat:

17 4 * * * root /opt/adguardhome/erneuern.sh >> /var/log/adguard-zertifikat.log 2>&1

Verifizieren: Ein Aufruf von Hand endet nach einer Sekunde mit Skip renewal: The certificate expires at …, solange keine Erneuerung fällig ist.

Was kostet der Umweg?

Wir haben vom Rechner im Heimnetz aus je 20 Abfragen gestellt, jede mit einer neuen Verbindung samt Verschlüsselung. Im Median brauchten DoT und DoH zum eigenen Server 63 Millisekunden, DoQ 69. DoT zu Quad9 lag mit 65 Millisekunden gleichauf. Klassisches DNS über UDP zu Quad9 war mit 21 Millisekunden schneller, weil der Verbindungsaufbau wegfällt. Der Ping zum VPS lag bei 16 Millisekunden. Im Alltag halten Telefone und Browser die verschlüsselte Verbindung offen, dann fällt der Aufbau nur einmal an.

Typische Fehler

Meldung oder BeobachtungUrsache und Lösung
listen tcp [::]:853: bind: address already in usebind_hosts enthält 0.0.0.0 und "::". Nur 0.0.0.0 eintragen, Schritt 3.
Name des DNS-Servers löst während der Zertifikatsprüfung nicht aufNur ein Wildcard-Eintrag in der Zone, der TXT-Eintrag hebelt ihn aus. Feste Einträge für dns und *.dns, Schritt 1.
lego bricht mit Zeitüberschreitung bei der Propagation abDer Nameserver braucht länger als NETCUP_PROPAGATION_TIMEOUT. Wert auf 1800 setzen.
Caddy meldet no valid A records found for dns.sedv-lab.deNeue Einträge noch nicht verteilt. Einige Minuten warten, dann caddy reload.
status: REFUSED für das eigene GerätClient-ID fehlt oder steht nicht in allowed_clients. Bei DoT den Namen ID.dns.sedv-lab.de verwenden, bei DoH den Pfad /dns-query/ID.
DNS-over-TLS won't be advertised via DDRKein Fehler. Zertifikat ohne IP-Adresse, DDR fällt weg.

Häufige Fragen

Warum nicht einfach Port 53 öffnen?

Ein offener Resolver auf Port 53 wird schnell gefunden und für Verstärkungsangriffe missbraucht. Manche Netze unterwegs blockieren Port 53 nach außen, und klassisches DNS kann jeder auf dem Weg mitlesen. DoT, DoH und DoQ sind verschlüsselt, und über die Client-ID beantwortet der Server nur Ihre Geräte.

Brauche ich alle drei Protokolle?

Nein. Apple-Geräte nutzen über Profile DoH oder DoT, Browser meist DoH. DoQ ist das jüngste Verfahren und wird noch von wenigen Programmen unterstützt. Alle drei kosten in AdGuard Home nur einen Port in der Konfiguration.

Sieht der VPS-Anbieter meine Anfragen?

Die Anfragen kommen verschlüsselt beim VPS an. AdGuard Home fragt die Upstream-Server im Beispiel ebenfalls verschlüsselt über DoH und DoT an. Das Abfrageprotokoll liegt aber auf dem VPS. Wer das nicht möchte, schaltet es in den Einstellungen ab oder kürzt die Aufbewahrung.

Geht das auch ohne Wildcard-Zertifikat?

Für DoH ja, denn dort steht die Client-ID im Pfad, und ein normales Zertifikat von Caddy genügt. Für DoT und DoQ ohne Wildcard müsste jede Client-ID als eigener Name ins Zertifikat, und bei jedem neuen Gerät wäre ein neues Zertifikat fällig.

Fazit

Ein eigener verschlüsselter DNS-Server auf dem VPS ist in einer Stunde eingerichtet und war im Test so schnell wie DoT bei Quad9. Die Arbeit steckt im Zertifikat: Für DoT und DoQ mit Client-ID braucht es ein Wildcard-Zertifikat über DNS-01, und dafür feste Einträge in der Zone und Geduld mit dem Nameserver. Mit der Liste zugelassener Clients und abgeschaltetem einfachen DNS bleibt der Server Ihr eigener und wird nicht zum offenen Resolver.

Weiterführende Anleitungen und Quellen

Server im NetzAdGuard HomeDNS-over-HTTPSDNS-over-TLSDNS-over-QUIClegoLet's EncryptWerbeblocker