Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Hetzner DynDNS Bridge für die Hetzner Console API

profile picture
Author
woehrl
Published
2026-08-07
Time to read
9 minutes reading time

About the author- S/W Architect, Full-Stack Senior Developer

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), dedizierte myip6-/myipv6-Parameter und protokollkonforme nochg-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 curl und SQLite3 (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 ServerName auf 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      Optional

Beispiel-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
done

Schritt 2 - Config anlegen

sudo cp hetzner_dyndns.config.php.dist hetzner_dyndns.config.php

Bearbeite 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_endpoint und api_order gehörten zur abgeschalteten Legacy-API und werden ignoriert. Es genügt, pro Realm ein console_token zu ergänzen — der Rest der Config kann bleiben.

Schritt 3 - Sicher ins Netz stellen

  • Halte hetzner_dyndns.config.php aus öffentlichen Repos und Verzeichnislisten heraus.
  • Stelle sicher, dass .htaccess die 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 auf update, wenn leer). DynDNS-Clients senden meist irgendeinen Usernamen plus das Passwort; per curl geht 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) oder nochg <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 Parameter myip6=<IPv6> bzw. myipv6=<IPv6> (Fritz!Box: myip=<ipaddr>&myip6=<ip6addr> in der Update-URL).
  • IPv6-only/DS-Lite: einfach nur die IPv6 senden (myip6=... ohne myip) — 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 .htaccess greift nicht.
  • Zone not found on console API: falscher zone_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_token pro Realm prüfen (DNS-Schreibrechte!) und mit debug das Log ansehen.

Nerd-Ecke (so funktioniert es wirklich)

  • Architektur im Überblick
    • Ein PHP-Skript, eine Config, eine SQLite-DB. DynDNS-Aufrufe (/nic/update oder /v3/update) werden via .htaccess auf hetzner_dyndns.php umgeschrieben.
    • 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 mit nochg, wenn sich nichts geändert hat — ganz ohne API-Call.

  • Konfig-Details
    • Gemeinsame Einstellungen: auth_user, auth_password, auth_realm, history_db sowie optional debug/debug_log.
    • Benachrichtigungen: notifications.enabled aktivieren, php oder smtp wählen, Empfänger setzen und entscheiden, ob bei Erfolg, Fehler oder beidem gemailt wird.
    • Realms: pro Realm ttl, zone_name und console_token. Setze zone_name, wenn der DynDNS-Endpunkt auf einer Subdomain liegt, du aber die Hauptzone aktualisierst.

  • API-Ablauf (Hetzner Console)
    1. 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.
    2. RRsets über /zones/{id}/rrsets (A und AAAA) holen.
    3. 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.
    4. set_records aufrufen, 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).
    5. Fehler bleiben als needs_sync markiert und werden vom Cron erneut versucht.

  • 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üssel dns_token, dns_endpoint und api_order werden ignoriert (bei aktivem debug mit Hinweis im Log).
    • Pro Realm ist jetzt ein console_token Pflicht; die SQLite-DB bleibt kompatibel und muss nicht angefasst werden.
    • Nach dem Update einmal php hetzner_dyndns.php --cron ausführen, um offene Jobs abzuarbeiten — der Cron-Aufruf über die Kommandozeile benötigt (und akzeptiert) keine HTTP-Zugangsdaten.

  • Mehrere Zonen und Realms
    • Lege pro Zone oder Subdomain einen Realm an. Beispiel: Realm gjsi.de und ddns.gjsi.de mit 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.php kann gecachte IPv4/IPv6 pro Realm ausgeben, ohne den HTTP-Endpunkt zu treffen.

  • Benachrichtigungen und Observability
    • E-Mails zu Erfolgen und Fehlern über PHP mail() oder SMTP.
    • debug und debug_log zeichnen alle API-Requests und -Responses plus Benachrichtigungsstatus auf.
    • Cron-Summen listen Gesamtzahl, Erfolge und Fehler pro Host.

  • Sicherheit und Deployment
    • .htaccess blockiert 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.

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue
Try Hetzner Cloud

Get €20/$20 free credit!

Valid until: 31 December 2026 Valid for: 3 months and only for new customers
Get started
Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more