netcup-DNS-Zone sichern, versionieren und zurückspielen mit DNSControl
Teil 11 der Reihe netcup Spezial: Die netcup-Zone jede Nacht mit DNSControl in Git sichern, Änderungen erkennen und nach einer Fehlbedienung gezielt zurückspielen. Mit der Falle, die beim Zurückspielen fremde Einträge löscht.
Geprüft am 04.11.2026 · für DNSControl 4.36.1, Docker 29.9.2, Git 2.43.0, Ubuntu 24.04.5
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

Ein falsch gelöschter MX-Eintrag oder ein überschriebener CNAME fällt oft erst auf, wenn Mails nicht mehr ankommen. Im CCP sehen Sie nur den aktuellen Stand Ihrer Zone, einen Verlauf früherer Stände haben wir dort nicht gefunden. Diese Anleitung zeigt, wie Sie Ihre netcup-Zone mit dem freien Werkzeug DNSControl über die DNS-API sichern, jede Änderung in Git sichtbar machen und einen früheren Stand gezielt zurückspielen. Dazu gehört eine Falle, in die man beim Zurückspielen leicht tappt.
Dies ist Teil 11 der Reihe „netcup Spezial“.
Testaufbau
Getestet haben wir am 04.11.2026 auf einem netcup VPS 500 G12.5 mit Ubuntu 24.04.5, Docker 29.9.2 und Git 2.43.0. DNSControl lief in Version 4.36.1 als Docker-Container. Die Domain sedv-lab.de liegt im klassischen netcup-DNS, ihre Zone enthielt beim Test rund 20 Einträge vom Typ A, AAAA, MX, CAA, TLSA und TXT. Für die Tests haben wir eigene Einträge unter bak-*.sedv-lab.de angelegt, verändert, gelöscht und zurückgespielt.
| Test | Ergebnis |
|---|---|
| Zone exportieren | alle Einträge gelesen, auch CAA und TLSA |
| Änderungen in Git erkennen | gelöschter und geänderter Eintrag im git diff sichtbar |
| Fehlbedienung zurückspielen | zwei gelöschte Einträge neu angelegt, ein falscher CNAME korrigiert |
| Aus einer älteren Sicherung zurückspielen | gezielt zwei Einträge wiederhergestellt, fremde Einträge unverändert |
| Sicherung per cron | Skript läuft unter cron ohne Anpassung |
Die öffentlichen Adressen des Testservers sind in den Ausgaben durch Beispieladressen ersetzt.
Voraussetzungen
- Eine Domain bei netcup im klassischen DNS mit den netcup-Nameservern
- Ein Legacy-API-Key und das API-Passwort aus dem CCP, wie in Teil 14 beschrieben
- Ein Linux-Rechner mit Docker und Git, der dauerhaft läuft
Die Befehle führen Sie als root oder mit sudo aus. Ein Hinweis vorab: netcup kennzeichnet die Legacy-API im CCP als auslaufend, und laut netcup-Support (04.11.2026) blockieren vorhandene Legacy-API-Keys die Umstellung eines Kontos auf CloudDNS.
Schritt 1: Zugangsdaten ablegen
Legen Sie einen Arbeitsordner an und darin eine Datei mit den Zugangsdaten, die nur root lesen darf:
mkdir -p /opt/zone && cd /opt/zone
(umask 077; nano netcup.env)
NETCUP_CUSTOMER_NUMBER=12345
NETCUP_API_KEY=IHR-API-KEY
NETCUP_API_PASSWORD=IHR-API-PASSWORT
DNSControl liest seine Zugänge aus creds.json. Werte, die mit $ beginnen, holt es aus Umgebungsvariablen. So bleibt die Datei frei von Geheimnissen und darf später mit ins Git:
{
"netcup": {
"TYPE": "NETCUP",
"api-key": "$NETCUP_API_KEY",
"api-password": "$NETCUP_API_PASSWORD",
"customer-number": "$NETCUP_CUSTOMER_NUMBER"
}
}
Der Legacy-API-Key gilt für alle Domains Ihres Kontos und darf auch schreiben. Legen Sie für die Sicherung einen eigenen Key an.
Verifizieren: ls -l netcup.env zeigt -rw-------.
Schritt 2: Die Zone exportieren
DNSControl gibt es als Docker-Image. Für einen ersten Export im Format von DNSControl:
docker run --rm --env-file netcup.env -v "$PWD":/dns -w /dns \
ghcr.io/stackexchange/dnscontrol:4.36.1 \
get-zones --format=js --out=dnsconfig.js netcup NETCUP sedv-lab.de
Die Reihenfolge der Angaben ist: Name aus creds.json, Anbieter, Zone. DNSControl gibt dabei die Warnung aus, man solle NETCUP künftig durch - ersetzen. In Version 4.36.1 brach der Aufruf mit - bei uns aber ab, mit der Meldung Arguments should be: credskey providername zone(s). Bleiben Sie deshalb bei NETCUP.
Wichtig zu wissen: Die DNSControl-Doku führt get-zones für netcup als nicht unterstützt, und der netcup-Anbieter wird nicht offiziell von den DNSControl-Entwicklern betreut. In Version 4.36.1 hat der Export im Test trotzdem alle Einträge gelesen. Prüfen Sie nach jedem Versionswechsel von DNSControl, ob die Vorschau direkt nach einem Export 0 corrections meldet.
Die erzeugte dnsconfig.js, bei uns gekürzt:
var DSP_NETCUP = NewDnsProvider("netcup", "NETCUP");
var REG_CHANGEME = NewRegistrar("none");
D("sedv-lab.de", REG_CHANGEME,
DnsProvider(DSP_NETCUP),
A("@", "203.0.113.10"),
MX("@", 10, "mail.sedv-lab.de."),
CAA("@", "issue", "letsencrypt.org"),
A("home", "203.0.113.10"),
A("mail", "203.0.113.10"),
TLSA("_25._tcp.mail", 3, 1, 1, "14d26e07935312d0e6b07c554dc257f73494d21af44fcf831046c3d897abc314"),
);
DNSControl nennt das selbst „a decent first draft“, einen brauchbaren ersten Entwurf. Im Test stimmte er mit der Zone überein: Eine Vorschau direkt nach dem Export meldete keine Abweichung. Eine TTL je Eintrag steht nicht darin, denn netcup kennt laut DNSControl-Doku nur eine TTL für die ganze Zone.
Legen Sie gleich ein Git-Verzeichnis an, schließen Sie die Zugangsdaten aus und halten Sie die Konfiguration fest:
git init -b main .
echo netcup.env > .gitignore
git add .gitignore creds.json dnsconfig.js
git -c user.name=admin -c user.email=root@localhost commit -m "DNSControl-Konfiguration"
Verifizieren: Die Vorschau aus Schritt 5 meldet direkt nach dem Export Done. 0 corrections. So war es auch im Test mit einem frischen Export, dazu kam nur der Hinweis can be simplified to NewDnsProvider("netcup"). Sie können das zweite Argument "NETCUP" in der Datei löschen.
Schritt 3: Jede Nacht in Git sichern
Für die Sicherung eignet sich das klassische Zonendatei-Format besser, denn es ist kompakt und gut lesbar. Legen Sie das Skript mit nano /opt/zone/zone-sichern.sh an. Es exportiert die Zone und legt nur bei einer Änderung einen Git-Commit an:
#!/bin/sh
# Sichert die Zonen als BIND-Datei und legt bei Änderungen einen Git-Commit an.
set -e
cd /opt/zone
for z in sedv-lab.de; do
docker run --rm --env-file netcup.env -v /opt/zone:/dns -w /dns \
ghcr.io/stackexchange/dnscontrol:4.36.1 \
get-zones --format=zone --out=export/$z.zone netcup NETCUP $z
done
git add export
if git diff --cached --quiet; then
echo "$(date +%F\ %T) keine Änderung"
else
git -c user.name=zone-sichern -c user.email=root@localhost commit -qm "Zonen-Export $(date +%F\ %T)"
echo "$(date +%F\ %T) Änderung gesichert: $(git log -1 --format=%h)"
fi
Weitere Domains tragen Sie in die Liste hinter for z in ein. Machen Sie das Skript ausführbar und starten Sie es einmal:
cd /opt/zone
mkdir -p export
chmod 700 zone-sichern.sh
./zone-sichern.sh
Der erste Lauf meldet Änderung gesichert, jeder weitere ohne Änderung keine Änderung. Davor steht jedes Mal die Warnung, NETCUP durch - zu ersetzen, siehe Schritt 2. Schlägt der Export fehl, bricht das Skript wegen set -e ab, und die Fehlermeldung landet im Log. Im Test mit einem falschen API-Passwort stand dort login to netcup DNS failed, please check your credentials, einen Commit gab es nicht.
Die Datei export/sedv-lab.de.zone sah bei uns so aus, gekürzt:
$ORIGIN sedv-lab.de.
$TTL 300
@ IN A 203.0.113.10
IN MX 10 mail.sedv-lab.de.
IN CAA 0 issue "letsencrypt.org"
bak-alias IN CNAME bak-web.sedv-lab.de.
bak-fremd IN TXT "im-ccp-angelegt"
bak-txt IN TXT "sicherung-test-1"
bak-web IN A 203.0.113.80
home IN A 203.0.113.10
mail IN A 203.0.113.10
_25._tcp.mail IN TLSA 3 1 1 14d26e07935312d0e6b07c554dc257f73494d21af44fcf831046c3d897abc314
Für den nächtlichen Lauf legen Sie /etc/cron.d/zone-sichern an:
17 3 * * * root /opt/zone/zone-sichern.sh >> /var/log/zone-sichern.log 2>&1
Im Test haben wir das Skript mit einer minütlichen cron-Zeile laufen lassen. Es lief unter cron ohne Anpassung und schrieb keine Änderung ins Log. Schauen Sie gelegentlich hinein: Steht dort tagelang keine neue Zeile, läuft die Sicherung nicht.
Die Sicherung liegt so nur auf einem Rechner. Schieben Sie das Git-Verzeichnis zusätzlich auf ein privates Repository oder in Ihre normale Datensicherung.
Verifizieren: git log --oneline zeigt einen Commit Zonen-Export mit Datum.
Schritt 4: Änderungen erkennen
Um eine Änderung im CCP nachzustellen, haben wir per API einen Eintrag gelöscht und bei einem anderen die Adresse geändert. Der nächste Lauf des Skripts legte sofort einen neuen Commit an, und git diff HEAD~1 zeigte genau diese beiden Änderungen (gekürzt):
bak-alias IN CNAME bak-web.sedv-lab.de.
-bak-fremd IN TXT "im-ccp-angelegt"
bak-txt IN TXT "sicherung-test-1"
-bak-web IN A 203.0.113.80
+bak-web IN A 203.0.113.81
Der Export liest den Stand über die API und damit den Stand, den das CCP zeigt. Er weicht unmittelbar nach einer Änderung also vom DNS ab, denn netcup veröffentlicht Änderungen gebündelt etwa alle zehn Minuten, wie wir in Teil 15 gemessen haben.
Verifizieren: git log -p export/ zeigt jede gesicherte Änderung mit Datum.
Schritt 5: Einträge mit DNSControl pflegen
DNSControl kann die Zone nicht nur lesen. Mit preview zeigt es, was es ändern würde, mit push setzt es die Datei in der Zone um. Ergänzen Sie in dnsconfig.js direkt unter DnsProvider(...) die Zeile NO_PURGE,, warum genau, steht in Schritt 7. Wir haben außerdem drei Testeinträge eingetragen:
DnsProvider(DSP_NETCUP),
NO_PURGE,
A("bak-web", "203.0.113.80"),
CNAME("bak-alias", "bak-web.sedv-lab.de."),
TXT("bak-txt", "sicherung-test-1"),
Die Vorschau:
docker run --rm --env-file netcup.env -v "$PWD":/dns -w /dns \
ghcr.io/stackexchange/dnscontrol:4.36.1 preview
3 corrections (netcup)
INFO#1: WARNING: This provider does not reliably support NO_PURGE
#1: + CREATE TXT bak-txt.sedv-lab.de "sicherung-test-1" ttl=0
#2: + CREATE A bak-web.sedv-lab.de 203.0.113.80 ttl=0
#3: + CREATE CNAME bak-alias.sedv-lab.de bak-web.sedv-lab.de. ttl=0
Done. 3 corrections.
Steht dort nur, was Sie erwarten, führen Sie denselben Befehl mit push statt preview aus. Im Test meldete jeder der drei Einträge SUCCESS!, der ganze Lauf dauerte eine Sekunde. Danach meldete eine neue Vorschau 0 corrections. Halten Sie die geänderte Datei in Git fest:
git -c user.name=admin -c user.email=root@localhost commit -am "NO_PURGE und neue Einträge"
Verifizieren: preview nach dem push zeigt Done. 0 corrections.
Schritt 6: Eine Fehlbedienung zurückspielen
Jetzt der Ernstfall: Wir haben per API zwei der drei Einträge gelöscht und den CNAME auf ein falsches Ziel gesetzt. Die Vorschau mit unveränderter dnsconfig.js fand genau diese drei Abweichungen:
#1: + CREATE TXT bak-txt.sedv-lab.de "sicherung-test-1" ttl=0
#2: + CREATE A bak-web.sedv-lab.de 203.0.113.80 ttl=0
#3: ± MODIFY CNAME bak-alias.sedv-lab.de: (falsch.sedv-lab.de. ttl=0) -> (bak-web.sedv-lab.de. ttl=0)
Ein push stellte alle drei Einträge in derselben Sekunde wieder her. Wie bei jeder Änderung über die API liefern die netcup-Nameserver den alten Stand noch einige Minuten aus.

Verifizieren: Im CCP stehen die Einträge wieder, und preview meldet 0 corrections.
Schritt 7: Warum NO_PURGE Pflicht ist
DNSControl behandelt die Datei als vollständige Beschreibung der Zone. Alles, was in der Zone steht und nicht in der Datei, löscht es beim push. Das betrifft jeden Eintrag, den jemand nach Ihrem letzten Export im CCP angelegt hat. Wir haben das nachgestellt: Per API kam der Eintrag bak-fremd dazu, der nicht in dnsconfig.js stand. Die Vorschau ohne NO_PURGE:
4 corrections (netcup)
#1: - DELETE TXT bak-fremd.sedv-lab.de "im-ccp-angelegt" ttl=0
#2: + CREATE TXT bak-txt.sedv-lab.de "sicherung-test-1" ttl=0
#3: + CREATE A bak-web.sedv-lab.de 203.0.113.80 ttl=0
#4: ± MODIFY CNAME bak-alias.sedv-lab.de: ...
Mit NO_PURGE blieb der Eintrag stehen, die Vorschau nannte ihn nur als Hinweis:
INFO#1: WARNING: This provider does not reliably support NO_PURGE
INFO#2: 1 records not being deleted because of NO_PURGE:
INFO#3: bak-fremd.sedv-lab.de. TXT "im-ccp-angelegt"
Nach dem push stand bak-fremd weiterhin in der Zone. Die Warnung in der ersten Zeile gibt DNSControl für netcup bei jedem Lauf aus, in dessen Datei NO_PURGE steht. Die allgemeine Doku zu NO_PURGE sagt dagegen, dass es seit Version 4 bei allen Anbietern funktioniert. Wann es bei netcup nicht greift, nennt die Meldung nicht. In unseren Läufen hat es funktioniert. Verlassen Sie sich trotzdem nicht darauf: Lesen Sie vor jedem push die Vorschau und brechen Sie ab, sobald dort ein DELETE steht, das Sie nicht wollen.
Verifizieren: Ihre Vorschau enthält die Zeile records not being deleted because of NO_PURGE, sobald die Zone Einträge hat, die nicht in Ihrer Datei stehen.
Schritt 8: Aus einer älteren Sicherung zurückspielen
Liegt der Fehler länger zurück, holen Sie den alten Stand aus Git. DNSControl liest Zonendateien mit dem Anbieter BIND. Ergänzen Sie creds.json um einen zweiten Eintrag, sie sieht danach so aus:
{
"netcup": {
"TYPE": "NETCUP",
"api-key": "$NETCUP_API_KEY",
"api-password": "$NETCUP_API_PASSWORD",
"customer-number": "$NETCUP_CUSTOMER_NUMBER"
},
"alt": {
"TYPE": "BIND",
"directory": "alt"
}
}
Im Ordner alt erwartet DNSControl die Zone als Datei mit dem Namen sedv-lab.de.zone.
Holen Sie die Zonendatei aus einem früheren Commit und wandeln Sie sie in das Format von DNSControl um:
mkdir -p alt
git log --oneline export/
git show bcf9030:export/sedv-lab.de.zone > alt/sedv-lab.de.zone
docker run --rm --env-file netcup.env -v "$PWD":/dns -w /dns \
ghcr.io/stackexchange/dnscontrol:4.36.1 \
get-zones --format=js --out=alt.js alt BIND sedv-lab.de
Statt bcf9030 setzen Sie die Kennung des Commits ein, den git log für den gewünschten Stand zeigt. Auch hier erscheint die Warnung, BIND durch - zu ersetzen. Wir haben BIND verwendet, die Umwandlung lief ohne Fehler. Aus alt.js übernehmen Sie nur die Einträge, die Sie zurückhaben wollen, in eine eigene Datei rueck.js. Wir haben alle vier Testeinträge übernommen, zwei davon unverändert:
var DSP_NETCUP = NewDnsProvider("netcup");
var REG_NONE = NewRegistrar("none");
D("sedv-lab.de", REG_NONE,
DnsProvider(DSP_NETCUP),
NO_PURGE,
CNAME("bak-alias", "bak-web.sedv-lab.de."),
TXT("bak-fremd", "im-ccp-angelegt"),
TXT("bak-txt", "sicherung-test-1"),
A("bak-web", "203.0.113.80"),
);
Vorschau und Rückspielen mit dieser Datei:
docker run --rm --env-file netcup.env -v "$PWD":/dns -w /dns \
ghcr.io/stackexchange/dnscontrol:4.36.1 preview --config rueck.js
2 corrections (netcup)
INFO#1: WARNING: This provider does not reliably support NO_PURGE
INFO#2: 17 records not being deleted because of NO_PURGE:
INFO#3: *.sedv-lab.de. AAAA 2001:db8:15:16ab:800:5eff:fe00:5301
...
#1: + CREATE TXT bak-fremd.sedv-lab.de "im-ccp-angelegt" ttl=0
#2: ± MODIFY A bak-web.sedv-lab.de: (203.0.113.81 ttl=0) -> (203.0.113.80 ttl=0)
Done. 2 corrections.
Die lange Liste der nicht gelöschten Einträge ist hier erwünscht: Es sind alle Einträge, die nicht in rueck.js stehen. DNSControl ändert nur die beiden Abweichungen.
Danach push --config rueck.js. Im Test waren beide Einträge sofort im alten Zustand, der nächste Lauf von zone-sichern.sh hielt die Rücksicherung als eigenen Commit fest. Der gezielte Weg ist sicherer als die ganze alte Zone zurückzuspielen: Alles, was seit der Sicherung richtig geändert wurde, bleibt unberührt.

Verifizieren: git diff HEAD~1 -- export/ nach dem nächsten Lauf zeigt genau die zurückgespielten Einträge.
Typische Fehler
| Meldung | Ursache und Lösung |
|---|---|
Arguments should be: credskey providername zone(s) | Bei get-zones steht - statt NETCUP als Anbieter. In 4.36.1 NETCUP angeben und die Warnung hinnehmen. |
WARNING: This provider does not reliably support NO_PURGE | Erscheint für netcup bei jeder Vorschau mit NO_PURGE. Kein Abbruchgrund, aber Anlass, die Vorschau genau zu lesen, siehe Schritt 7. |
- DELETE für Einträge, die Sie behalten wollen | NO_PURGE fehlt, oder die Datei ist älter als die Zone. Nicht pushen, NO_PURGE ergänzen oder neu exportieren. |
can be simplified to NewDnsProvider("netcup") | Hinweis, kein Fehler. Das zweite Argument in dnsconfig.js löschen. |
ttl=0 in jeder Zeile | netcup kennt keine TTL je Eintrag, die TTL gilt für die ganze Zone. Kein Fehler. |
login to netcup DNS failed, please check your credentials | API-Key oder API-Passwort falsch, oder der Key wurde im CCP gelöscht. Werte in netcup.env prüfen. |
Häufige Fragen
DNSControl oder Ansible?
Das Ansible-Modul aus Teil 14 pflegt einzelne Einträge und rührt den Rest nicht an. DNSControl kennt die ganze Zone, kann sie exportieren und Unterschiede anzeigen. Für Sicherung und Vergleich ist DNSControl das passendere Werkzeug.
Liegen in der Sicherung Geheimnisse?
Die Zugangsdaten nicht, sie stehen nur in netcup.env, die per .gitignore ausgeschlossen ist. Die Zone selbst ist öffentlich abfragbar. Interne Namen, die Sie lieber nicht im Netz sehen wollen, landen aber ebenfalls in der Datei. Schieben Sie das Repository deshalb nur auf einen privaten Speicherort.
Fazit
Mit DNSControl und einem kleinen Skript wird die netcup-Zone versioniert: Jede Nacht ein Export in Git, jede Änderung als Commit sichtbar, ein früherer Stand in wenigen Befehlen zurück. Im Test hat das Zurückspielen nach einer Fehlbedienung und aus einer älteren Sicherung funktioniert. Entscheidend ist NO_PURGE zusammen mit einem Blick in die Vorschau, denn ohne diese Zeile hätte DNSControl einen Eintrag gelöscht, den jemand nach dem Export angelegt hatte.
Weiterführende Anleitungen und Quellen
- Teil 14: netcup-DNS mit Ansible pflegen
- Teil 15: Wildcard-Zertifikat über die netcup-DNS-API
- Teil 13: DynDNS mit einer netcup-Domain
- DNS-Records bei netcup im CCP verwalten
- Ubuntu-Server mit Docker als Grundlage
- netcup: DNS-API (Legacy)
- DNSControl: Anbieter netcup
- DNSControl: get-zones
- DNSControl: NO_PURGE
- DNSControl: Anbieter BIND


