Einführung
Deine IPv4/IPv6 soll folgen wie ein treuer Hund, ohne dass du DNS von Hand pflegen musst? Dieses kleine PHP-Skript spielt DynDNS-Server, spricht die Hetzner Console API und versteht sich mit Routern wie einer Fritz!Box. Zuerst kommt die Schritt-für-Schritt-Anleitung für Einsteiger, danach die Nerd-Ecke mit den Details.
Update August 2026: Hetzner hat die alte DNS Console (
dns.hetzner.com) samt Legacy-API im Mai 2026 abgeschaltet; verbliebene Zonen wurden automatisch in die Hetzner Console migriert. Dieses Tutorial und das Skript wurden entsprechend aktualisiert und nutzen ausschließlich die Hetzner Console API. Außerdem neu: Unterstützung für IPv6-only-Anschlüsse (DS-Lite), dediziertemyip6-/myipv6-Parameter und protokollkonformenochg-Antworten. Wer noch eine ältere Version des Skripts einsetzt, findet Migrationshinweise in der Nerd-Ecke.
Voraussetzungen
- Ein Hetzner-Account mit mindestens einer DNS-Zone in der Hetzner Console.
- PHP mit den Erweiterungen
curlundSQLite3(Webspace oder kleine VM reicht). - Einen Ort für das PHP-Skript und einen Cronjob alle paar Minuten.
- Einen Client, der eine DynDNS-URL aufrufen kann (Router, NAS oder ein einfacher curl-Aufruf).
- Die A-/AAAA-Records, die aktualisiert werden sollen, müssen in der Zone bereits existieren — das Skript ändert nur Werte und legt bewusst keine neuen Records an.
Schritt 0 - Beispiel-Setup auf Debian/Ubuntu
Falls du von einem blanken Debian/Ubuntu startest, bringen dich diese Befehle zu einem testbaren Endpunkt:
- Voraussetzungen installieren und Ordner für Skripte erstellen
sudo apt update sudo apt install -y apache2 libapache2-mod-php php-cli php-curl php-sqlite3 sudo a2enmod rewrite sudo mkdir -p /var/www/hetzner-ddns sudo chown -R www-data:www-data /var/www/hetzner-ddns
-
Apache-Seite erstellen
Passe
ServerNameauf deinen DynDNS-Endpunkt an. In Produktion solltest du HTTPS (z. B. Let's Encrypt) nutzen.cat <<'EOF' | sudo tee /etc/apache2/sites-available/hetzner-ddns.conf <VirtualHost *:80> ServerName ddns.example.com DocumentRoot /var/www/hetzner-ddns <Directory /var/www/hetzner-ddns> AllowOverride All Require all granted </Directory> </VirtualHost> EOF
- Seite aktivieren
sudo a2ensite hetzner-ddns sudo apachectl configtest sudo systemctl reload apache2
Schritt 1 - Dateien besorgen
Nutze die zu diesem Tutorial gebündelten Dateien in tutorials/hetzner-ddns-bridge/scripts:
Gespiegelt von https://github.com/woehrl/hetzner-dyndns, Commit
fbb3728. Kopiere sie auf deinen Webspace oder eine kleine VM.
Für das Beispiel-Setup aus "Schritt 0" müssen die Dateien in /var/www/hetzner-ddns gespeichert werden.
Du brauchst mindestens:
/var/www/hetzner-ddns
├─ hetzner_dyndns.php
├─ hetzner_dyndns.config.php.dist
├─ .htaccess Die Datei von GitHub
└─ hetzner_dyndns_listhosts.php OptionalBeispiel-Befehle:
export path="https://raw.githubusercontent.com/hetzneronline/community-content/refs/heads/master/tutorials/hetzner-ddns-bridge/scripts"
cd /var/www/hetzner-ddns
# Führe folgenden Befehl im Terminal aus,
# um die Dateien zu bestimmen
files=(
hetzner_dyndns.php
hetzner_dyndns.config.php.dist
.htaccess
hetzner_dyndns_listhosts.php
)
# Führe folgenden Befehl im Terminal aus,
# um die zuvor bestimmten Dateien zu kopieren
for f in "${files[@]}"; do
curl "$path/$f" | sudo tee "$f" >/dev/null
doneSchritt 2 - Config anlegen
sudo cp hetzner_dyndns.config.php.dist hetzner_dyndns.config.phpBearbeite hetzner_dyndns.config.php:
| Beschreibung | |
|---|---|
| auth_user | Optional: Username für HTTP Basic Auth. Wenn leer, wird update verwendet. |
| auth_password | Bestimme ein starkes, geteiltes Passwort. Dein Router nutzt es. |
| console_token | Erstelle in der Hetzner Console ein Projekt-API-Token mit Lese-/Schreibrechten für DNS und trage es hier ein. Pflichtfeld pro Realm. |
| zone_name | Wenn die IP-Adresse für eine Subdomain (z.B. sub.example.com) aktualisiert werden soll, muss hier die Hauptdomain (z.B. example.com) angegeben werden. |
| auth_realm | Optional: Wähle ein Label (z. B. "dynbridge"). |
| history_db | Optional: Zeige auf einen beschreibbaren Pfad (z. B. DIR . '/hetzner_dyndns.sqlite3'). |
| TTL | Optional: Passe die TTL pro Realm an, wenn es schneller oder langsamer propagieren soll. |
Hinweis für Bestandsinstallationen: Die früheren Einstellungen
dns_token,dns_endpointundapi_ordergehörten zur abgeschalteten Legacy-API und werden ignoriert. Es genügt, pro Realm einconsole_tokenzu ergänzen — der Rest der Config kann bleiben.
Schritt 3 - Sicher ins Netz stellen
- Halte
hetzner_dyndns.config.phpaus öffentlichen Repos und Verzeichnislisten heraus. - Stelle sicher, dass
.htaccessdie DynDNS-Endpunkte auf das Skript umschreibt, damit alte Clients funktionieren:RewriteEngine On RewriteRule ^(nic/update|v3/update)$ hetzner_dyndns.php [L,QSA] - Falls nötig, sorge dafür, dass der PHP-User in die SQLite-Datei und das Debug-Log schreiben darf.
Schritt 4 - Update testen
- Baue die Test-URL (ersetze Domain und Host):
https://dein-ddns-host.example.com/nic/update?hostname=myhost.example.com&myip=203.0.113.10 - Nutze HTTP Basic Auth mit
auth_user/auth_password(User defaultet aufupdate, wenn leer). DynDNS-Clients senden meist irgendeinen Usernamen plus das Passwort; percurlgeht es so:curl -u user:deinPasswort \ "https://dein-ddns-host.example.com/nic/update?hostname=myhost.example.com&myip=$(curl -4 https://ip.hetzner.com)" - Erfolgreiche Antworten sind
good <ip>(aktualisiert) odernochg <ip>(nichts zu tun). Alles andere: ins Debug-Log schauen. - Kommt „No matching rrset found", existiert der A-/AAAA-Record noch nicht: einmalig in der Hetzner Console anlegen, dann klappt das Update.
Schritt 5 - Automatisieren
- Cron alle 5 Minuten (nach Bedarf anpassen):
*/5 * * * * php /path/to/hetzner_dyndns.php --cron --realm=default - Richte deinen Router oder dein NAS auf dieselbe
nic/update-URL mit dem gesetzten Passwort ein. - IPv4 und IPv6 zusammen: entweder kommagetrennt über
myip=<IPv4>,<IPv6>oder über einen dedizierten Parametermyip6=<IPv6>bzw.myipv6=<IPv6>(Fritz!Box:myip=<ipaddr>&myip6=<ip6addr>in der Update-URL). - IPv6-only/DS-Lite: einfach nur die IPv6 senden (
myip6=...ohnemyip) — das Skript aktualisiert dann ausschließlich den AAAA-Record und schreibt bewusst keine Carrier-IPv4 in den A-Record. - Legacy-Clients dürfen weiterhin
X-Authentication: <passwort>oder?p=<passwort>(nur Passwort) senden, empfohlen ist Basic Auth.
Schritt 6 - Kurzer Troubleshooting-Spickzettel
- 401 oder Auth-Prompt: Passwort stimmt nicht oder
.htaccessgreift nicht. Zone not found on console API: falscherzone_name, Zone liegt in einem anderen Console-Projekt oder das Token gehört zum falschen Projekt.No matching rrset found: den A-/AAAA-Record einmalig in der Zone anlegen — das Skript legt bewusst keine Records an.- SQLite-Schreibfehler: Dateirechte anpassen oder DB in einen beschreibbaren Pfad verschieben.
- Nichts ändert sich:
console_tokenpro Realm prüfen (DNS-Schreibrechte!) und mitdebugdas Log ansehen.
Nerd-Ecke (so funktioniert es wirklich)
- Architektur im Überblick
- Ein PHP-Skript, eine Config, eine SQLite-DB. DynDNS-Aufrufe (
/nic/updateoder/v3/update) werden via.htaccessaufhetzner_dyndns.phpumgeschrieben. - Das Skript authentifiziert mit Benutzername/Passwort, parst Hostname und optionales
realm, cached den letzten IP-Stand und die Zonen-ID in SQLite und antwortet sofort mitnochg, wenn sich nichts geändert hat — ganz ohne API-Call.
- Ein PHP-Skript, eine Config, eine SQLite-DB. DynDNS-Aufrufe (
- Konfig-Details
- Gemeinsame Einstellungen:
auth_user,auth_password,auth_realm,history_dbsowie optionaldebug/debug_log. - Benachrichtigungen:
notifications.enabledaktivieren,phpodersmtpwählen, Empfänger setzen und entscheiden, ob bei Erfolg, Fehler oder beidem gemailt wird. - Realms: pro Realm
ttl,zone_nameundconsole_token. Setzezone_name, wenn der DynDNS-Endpunkt auf einer Subdomain liegt, du aber die Hauptzone aktualisierst.
- Gemeinsame Einstellungen:
- API-Ablauf (Hetzner Console)
- Zone über
/zones?name=<zone>mit dem Hetzner Console API-Token finden — die Zonen-ID wird danach in SQLite gecacht, gealterte IDs heilen sich per erneutem Lookup selbst. - RRsets über
/zones/{id}/rrsets(A und AAAA) holen. - Den RRset-Namen exakt mit dem Host abgleichen (inkl.
@am Apex). Es wird nur der exakt passende Record aktualisiert; Fallbacks auf Eltern-Records gibt es absichtlich nicht. set_recordsaufrufen, um die IPs zu ersetzen — und zwar nur für die Adressfamilien, die der Client tatsächlich geliefert hat (IPv4-only, Dual-Stack oder IPv6-only).- Fehler bleiben als
needs_syncmarkiert und werden vom Cron erneut versucht.
- Zone über
- Migration von älteren Skript-Versionen
- Die Legacy-API (
dns.hetzner.com/api/v1) wurde von Hetzner im Mai 2026 abgeschaltet; der zugehörige Code-Pfad ist entfernt. Die Config-Schlüsseldns_token,dns_endpointundapi_orderwerden ignoriert (bei aktivemdebugmit Hinweis im Log). - Pro Realm ist jetzt ein
console_tokenPflicht; die SQLite-DB bleibt kompatibel und muss nicht angefasst werden. - Nach dem Update einmal
php hetzner_dyndns.php --cronausführen, um offene Jobs abzuarbeiten — der Cron-Aufruf über die Kommandozeile benötigt (und akzeptiert) keine HTTP-Zugangsdaten.
- Die Legacy-API (
- Mehrere Zonen und Realms
- Lege pro Zone oder Subdomain einen Realm an. Beispiel: Realm
gjsi.deundddns.gjsi.demit unterschiedlichen Tokens und TTLs. - Nutze
realm=<name>im DynDNS-Query-String, wenn du mehrere Domains über denselben Endpunkt bedienst. - Der optionale CLI-Helfer
hetzner_dyndns_listhosts.phpkann gecachte IPv4/IPv6 pro Realm ausgeben, ohne den HTTP-Endpunkt zu treffen.
- Lege pro Zone oder Subdomain einen Realm an. Beispiel: Realm
- Benachrichtigungen und Observability
- E-Mails zu Erfolgen und Fehlern über PHP
mail()oder SMTP. debugunddebug_logzeichnen alle API-Requests und -Responses plus Benachrichtigungsstatus auf.- Cron-Summen listen Gesamtzahl, Erfolge und Fehler pro Host.
- E-Mails zu Erfolgen und Fehlern über PHP
- Sicherheit und Deployment
.htaccessblockiert direkte Zugriffe auf PHP und SQLite und lässt nur die DynDNS-Endpunkte durch.- Tokens nur auf DNS scopen und regelmäßig rotieren.
- Passwortvergleiche laufen timing-sicher über
hash_equals(). - SQLite-DB und Debug-Log müssen für den PHP-User schreibbar sein; nach Möglichkeit außerhalb öffentlicher Webroots ablegen.
- DynDNS-Endpunkt per HTTPS bereitstellen. Reines HTTP würde dein Passwort verraten.
?p=<passwort>landet zudem in Server-Logs — wenn dein Client Basic Auth kann, nutze Basic Auth.
Ergebnis
Die Bridge liefert dir einen freundlichen DynDNS-Endpunkt auf Basis der Hetzner Console API — inklusive IPv6-only-Support, Retry-Logik und sauberem DynDNS2-Protokoll. Starte mit dem Schnellstart, damit Updates laufen, und wirf dann einen Blick in die Nerd-Ecke, wenn du Realms, Benachrichtigungen oder die Migration von einer älteren Skript-Version feintunen willst.