Umzug auf Nova

Wie du eine bestehende Installation der Legacy-Basis auf neue Nova-Hosts umziehst – mit Generalprobe, Cutover, Fallback-Fenster und optionaler Übernahme der Hetzner Primary IPs.

Hinweis: Im Release Candidate empfehlen wir, den Umzug zuerst mit einer Kopie auf Testservern durchzuspielen. Für produktive Umzüge begleiten wir dich auf Wunsch – schreib an hello@novacontrolpanel.com.

So funktioniert der Umzug

Nova zieht eine Installation deines bisherigen Hosting-Panels (im Folgenden „Legacy-Basis“, die alten Server „Quellserver“) auf frisch installierte Nova-Hosts um. Unterstützte Versionen der Legacy-Basis nennen wir dir bei der Planung des Umzugs.

Die Quellserver bleiben maßgeblich und unberührt, bis du umschaltest. Nova wird auf neuen Hosts installiert, die Metadaten werden importiert und die Daten kopiert, während die Quellserver weiter ausliefern. Beim Cutover wird die Quelle eingefroren, ein letztes Delta läuft, Nova aktiviert alles, und DNS/MX (oder die Hetzner Primary IPs) wechseln zu Nova. Bis zum Commit trägt ein Fallback jede Änderung, die auf Nova gemacht wurde, zurück auf die Quellserver.

PhaseMaßgeblichBefehleRückweg
P1 ImportQuellepreflight, plan, import, verify, delta, statusrollback (entfernt die Nova-Seite)
P2 Cutoverwechseltcutover --legacy-frozen (wiederholen, solange Exit 75)cutover --abort (bevor das Fenster öffnet)
P3 Fallback-Fenster (Standard 14 Tage, 1 bis 90)Nova; nur Inhaltsänderungenstatusfallback --legacy-mx-holding
P4 CommittedNovacommit --confirm-irreversiblekeiner

In P3 lehnt Nova strukturelle Änderungen umgezogener Mandanten ab (Sites, Postfächer, Datenbanken, Zonen, Benutzer anlegen oder löschen; Planwechsel) mit 409 NATIVE_IMPORT_FALLBACK_WINDOW_OPEN. Inhalte (Dateien, Mail, Datenbankinhalte, Passwörter, DNS-Einträge) darfst du ändern; ein Fallback trägt sie zurück.

Befehlsübersicht

Alle Befehle laufen als root auf dem Nova-Control-Host:

sh
NI="php /opt/nova-controlpanel/application/current/src/api/application/bin/nova-import.php"
$NI preflight --profile import.json
$NI plan      --profile import.json --run "$RUN"
$NI import    --run "$RUN" --plan <plan_sha256>
$NI verify    --run "$RUN"
$NI delta     --run "$RUN"
$NI status    --run "$RUN"
$NI cutover   --run "$RUN" --legacy-frozen        # oder: cutover --run "$RUN" --abort
$NI fallback  --run "$RUN" --legacy-mx-holding    # --legacy-mx-holding nur bei Läufen mit Mail-Domains
$NI commit    --run "$RUN" --confirm-irreversible
$NI rollback  --run "$RUN"                        # nur vor der ersten Cutover-Aktivierung
  • --profile ist ein Dateiname in /etc/nova-controlpanel/import/ (oder ein absoluter Pfad darin). Spätere Befehle finden das Profil über den Digest, den der Lauf gespeichert hat.
  • RUN ist imp_ plus 43 Zeichen aus A-Z a-z 0-9 _ -, von dir gewählt, z. B. RUN=imp_$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=').
  • Ausgabe: JSON-Receipts auf stdout; plan schreibt zusätzlich eine Zusammenfassung auf stderr.
  • status ist rein lesend und sperrt nichts; er zeigt Phase, Gate, die Schritte des letzten Versuchs, rollback_allowed, das Ende des Fallback-Fensters und die letzten Fehler.
Exit-CodeBedeutung
0erledigt
3abgelehnt (preflight, plan) oder fehlgeschlagen (import, verify, delta)
75ausstehend (Node-Effekte oder Datenläufe noch nicht abgeschlossen): denselben Befehl erneut ausführen
64Aufruffehler
77nicht root
1sonstiger Fehler mit NOVA_IMPORT_*-Code auf stderr

Wichtig: Aktualisiere Nova nicht zwischen plan und commit. Ein Lauf ist an den installierten Kandidaten gebunden. Nach einem Upgrade lehnen import, verify, delta, cutover und der Rückweg von fallback mit NOVA_IMPORT_RUN_CANDIDATE_CHANGED ab. Vor der ersten Aktivierung bleibt dann nur rollback und ein neuer Lauf, danach ist kein Fallback mehr möglich und nur noch commit. Schließe den Umzug also vor dem nächsten Nova-Update ab.

1. Vorbereitung (Tage vorher)

1.1 Anforderungen an die Quelle

  • Die Quelle muss eine unterstützte Version der Legacy-Basis sein; andere Versionen scheitern in preflight oder plan. Ist eine Aktualisierung nötig, führst du sie mit dem Updater der Legacy-Basis selbst durch.
  • Ein Quellserver mit Diensten pro Nova-Node. Mehrere Quellserver auf einem Node werden abgelehnt (PLATFORM_SERVER_NODE_SHARED).
  • Umgezogene Websites erhalten das Nova-Layout /var/www/nova-controlpanel/wst_<ref>/ mit web/ und private/ (siehe Pfade umgezogener Websites).
  • Nicht unterstützte Typen lehnt plan ab (RESOURCE_TYPE_UNSUPPORTED: XMPP, APS, OpenVZ, iptables).

Wichtig: Vertraue der Quelle, bevor du sie umziehst. In 1.0 spielt der Import Datenbank-Dumps mit administrativen Rechten auf dem Zielnode ein. Prüfe die Integrität der Quellserver: aktuelle Updates, keine unbekannten Root-Logins, SSH-Schlüssel, Cronjobs oder Systembenutzer. Im Zweifel setzt du die Quelle vor dem Umzug neu auf. Ein eigenes Import-Konto pro Datenbank ist für 1.1 geplant.

Pfade umgezogener Websites

QuellserverNovaWie
/var/www/clients/clientN/webN/web/var/www/nova-controlpanel/wst_<ref>/webbei jedem Durchlauf (Import, delta, letzter Durchlauf von cutover, Rückweg von fallback)
/var/www/clients/clientN/webN/private/var/www/nova-controlpanel/wst_<ref>/privatedieselben Durchläufe und Regeln wie web/: keine setuid-Dateien, keine Symlinks aus dem Baum heraus, Eigentümer und Modus der Nova-Identität der Site, zählt zum Speicherlimit der Site
cgi-bin/, log/, ssl/, tmp/ der Sitenicht übernommenNova schreibt eigene Logs und Zertifikate
/var/www/clients/clientN/webNSymlink auf wst_<ref>beim Cutover angelegt, von fallback, rollback und beim Löschen der Site entfernt
/var/www/<domain>Symlink auf wst_<ref>mit der Site, wie bei jeder Nova-Website (Tagesbetrieb)
–/var/www/clients/<customer>/<domain>mit der Site: der lesbare Pfad, den das Panel zeigt
  • Eine Quell-Site ohne private/ überspringt diesen Teil. Hat die Nova-Site später Dateien in private/, lehnt fallback vor jeder Übertragung mit NOVA_IMPORT_TRANSFER_LEGACY_PRIVATE_ABSENT ab: lege private/ auf der Quell-Site an (Eigentümer: Site-Benutzer) und starte fallback erneut.
  • Weil /var/www/clients/clientN/webN in den Nova-Baum zeigt, funktionieren absolute Pfade der Quelle in Dateien unter web/ und private/, in umgezogenen Cronjobs und in Anwendungskonfigurationen nach dem Cutover weiter. PHP braucht dafür nichts (open_basedir prüft den aufgelösten Pfad). In einem Chroot (Chroot-Cronjob, Jailkit-Shell, PHP-FPM-Chroot) sieht der Prozess wie bisher /web, /private, /tmp.
  • Der zusätzliche Standard-Link /var/www/clients/clientN/<domain> der Legacy-Basis wird nicht angelegt; nutze /var/www/<domain> oder den Kompatibilitätspfad.
  • plan listet pro Site die Links (paths.links) und jeden absoluten Quellpfad in umgezogenen Cronjobs (paths.cron_paths, die Befehle selbst werden nie ausgegeben), markiert als compat_symlink (vom Kompatibilitäts-Symlink abgedeckt), chrooted_job (sieht die Jail-Sicht) oder not_moved (Pfad einer Site außerhalb dieses Laufs: Befehl vor dem Cutover anpassen). Zwei Sites verschiedener Quellserver, die auf einem Nova-Node denselben Pfad /var/www/clients/clientN/webN bräuchten, lehnt plan ab (WEB_COMPAT_PATH_COLLISION).
  • Ein Pfad, der auf dem Nova-Node schon existiert, wird nie überschrieben: die Aktivierung stoppt mit NOVA_WEB_PATH_ALIAS_COLLISION. Räume den Pfad auf dem Node weg und führe denselben cutover erneut aus (zweimal: der erste Aufruf reiht den nächsten Versuch ein).

1.2 Ziel-Fleet

  1. Installiere die Nova-Fleet auf neuen Hosts (Installation): ein Nova-Node pro Quellserver mit Diensten, mit passenden Rollen. Bei Übernahme der Primary IPs muss jeder Nova-Node am selben Hetzner-Standort und im selben privaten Netz wie sein Quellserver liegen.
  2. Installiere vor der Aufnahme des zweiten Nodes eine Lizenz für die gesamte Node-Anzahl (Lizenz).
  3. Aktiviere jede Ebene, die die Quelle nutzt (Web, Mail, Datenbank, DNS, Benutzerzugänge), und prüfe die Fleet mit den Checklisten (Tagesbetrieb).
  4. Ergänze auf den Ziel-Nodes die Import-Capabilities in NOVA_AGENT_WRITE_CAPABILITIES (nur während des Umzugs, siehe Tabelle unten).
  5. Lege den SSH-Schlüssel je Quellserver aus dem Profil als root mit 0600 auf jedem Ziel-Node ab: /etc/nova-controlpanel-node-agent/import-transfer/ssh-server-<id>.key (Web, Mail) und /etc/nova-controlpanel-node-agent/import/ssh-server-<id> (Datenbank). Nur für einen Fallback zusätzlich den separaten Fallback-Schlüssel als ...import-transfer/ssh-server-<id>.fallback.key und ...import/ssh-server-<id>-fallback.
  6. PHP-Runtimes: Jede PHP-Version, die eine umgezogene Site nutzt, muss vor dem Import auf ihrem Nova-Node installiert sein – php<v>-fpm für PHP-FPM, php<v>-cgi für FastCGI. plan listet sie pro Node unter php_runtimes. Eine als node_default_only markierte Version ist entweder die eigene PHP-Serie des Nodes (Ubuntu 24.04: 8.3, Debian 13: 8.4) oder von dir zu installieren. Lass die php-Alternative des Nodes auf seiner eigenen Serie (update-alternatives --set php /usr/bin/php8.3 unter Ubuntu 24.04), sonst lehnt das nächste Nova-Upgrade seinen Preflight ab. Eine vorhandene Runtime gleicher Version, aber mit anderem FPM-Layout, deaktiviert oder fehlerhaft, wird abgelehnt (PLATFORM_PHP_RUNTIME_VERSION_SCOPE_EXISTS): erst angleichen oder reparieren.
  7. Web-Quota und Kernel-Updates: Das Quota-Dateisystem braucht die Kernel-Module quota_v2/quota_tree, die Ubuntu-Cloud-Kernel nur in linux-modules-extra-<kernel> mitbringen. Der Hook /etc/kernel/postinst.d/nova-web-quota-module lässt die Konfiguration eines Kernels ohne diese Module scheitern, damit er nie Boot-Standard wird. Danach: apt-get install --no-install-recommends linux-modules-extra-<kernel> und dpkg --configure -a.
  8. DNS-Node-Endpunkte: Veröffentliche vor dem Cutover den Endpunkt jedes Nova-DNS-Nodes, der importierte Zonen erhält – PUT /api/next/v1/dns-node-endpoints/{node_id} mit {"nameserver_fqdn":"ns1.example.net.","node_id":"...","transfer_ipv4":"<private address>","transfer_ipv6":null} oder im Panel unter Systemverwaltung → DNS-Server & TSIG. Der Import legt ihn nicht an.

Import-Capabilities für Schritt 4:

NodeCapability
Web- und Mail-Nodesimport.transfer.execute.v1
Datenbank-Nodesdatabase.import.v1
der Mail-Node, der eine Mail-IP der Quelle übernimmtmail.source-address.configure.v1

1.3 Zugriff auf die Quelle (auf den Quellservern)

  • Ein MariaDB-Konto nur mit SELECT-Rechten auf die Datenbank der Legacy-Basis, über verifiziertes TLS oder einen Unix-Socket. Bei IP-Übernahme trägst du die private Adresse des Quellservers als Datenbank-Host ein.
  • Pro Quellserver ein SSH-Konto nova-import, dessen Schlüssel nur das Source-Gate /usr/local/sbin/nova-import-source-gate ausführen darf, mit den passenden authorized_keys- und sudoers-Zeilen. Das Source-Gate und die genauen Zeilen erhältst du mit Nova. Der Host-Key wird im Profil festgelegt (host_key_sha256).
  • Nur wenn du ein Fallback-Fenster willst: ein Fallback-Writer-Konto mit genau den vorgesehenen Spaltenrechten sowie separater Fallback-SSH-Schlüssel und Restore-Konto. Ohne fallback_writer wird fallback abgelehnt (NOVA_IMPORT_FALLBACK_WRITER_UNCONFIGURED).

1.4 Import-Verzeichnis und Profil (Nova-Control-Host)

sh
install -d -m 0700 /etc/nova-controlpanel/import
umask 077
openssl rand -hex 32 > /etc/nova-controlpanel/import/id-key     # einmal pro Ziel; nie ändern
# Secrets: source-db-password, optional source-db-ca.pem, ssh-server-<id>, optional fallback-db-password (alle 0600 root)

Schreib anschließend /etc/nova-controlpanel/import/import.json: Quelldatenbank, ein Eintrag pro Quellserver mit SSH-Zugang (optional mail_relay und addresses), node_mapping, fallback_window_days, fallback_writer. Eine kommentierte Vorlage stellen wir dir mit Nova bereit.

1.5 Adressplan: Übernahme oder Neuzuordnung

Jede Adresse, die ein Quellserver bindet (Server-IPs, IP-Zuordnungen, vHost-Adressen), muss geplant sein, sonst lehnt plan ab (ADDRESS_PLAN_MISSING):

PlanBedeutungDNS, SPF, PTR
takeoverdie Hetzner Primary IPv4 (und IPv6) des Quellservers wandert beim Cutover auf den zugeordneten Nova-Nodenichts ändert sich: A/AAAA, MX, SPF und PTR bleiben an der Adresse
remap:<nova address>die Adresse bleibt beim Quellserver; Nova bindet eine eigeneCutover schreibt A/AAAA und SPF ip4:/ip6: in den importierten Zonen um; PTR der Nova-Adresse und extern gehostete Zonen pflegst du selbst

Für remap gilt:

  • Die Nova-Adresse muss bereits auf dem zugeordneten Nova-Node konfiguriert sein (sichtbar in ip address show).
  • Jede Adressfamilie der Quelle braucht eine Nova-Adresse derselben Familie. Ein Quellserver mit IPv4 und IPv6 braucht ein IPv4- und ein IPv6-Ziel. Gib dem Node notfalls zuerst eine (Hetzner Primary) IPv6; dafür muss der Server ausgeschaltet sein.

Für takeover siehe Hetzner-Primary-IP-Übernahme: Führe dort check aus, bis er besteht, und set-auto-delete-off.

1.6 DNS und Mail vorbereiten

  • Senke die TTL jedes Eintrags, der sich ändern wird (A/AAAA neu zugeordneter Adressen, MX, NS und Glue bei Nameserver-Wechsel), mindestens eine alte TTL vor dem Cutover auf 300 s.
  • Neu zugeordnete Mail-Adresse: Setze vor dem Cutover den Reverse-DNS der Primary IP des Nova-Nodes in der Hetzner-Konsole auf den Mail-Hostnamen und ergänze die neue Adresse in SPF-Einträgen extern gehosteter Zonen.
  • DKIM-Schlüssel ziehen mit ihrem bisherigen Selector um; hier ist nichts vorzubereiten.

2. Import und Generalprobe (P1, Quelle liefert weiter aus)

sh
$NI preflight --profile import.json            # Exit 0: passed
$NI plan --profile import.json --run "$RUN"    # Exit 0: planned; plan_sha256 notieren
$NI import --run "$RUN" --plan <plan_sha256>   # fortsetzbar; wiederholen, bis keine Arbeit mehr aussteht
$NI verify --run "$RUN"                        # Zähler, Digests, Mandanten- und Capability-Parität
$NI delta --run "$RUN"                         # beliebig oft bis zum Cutover
  • Behebe jede Ablehnung von preflight und plan an der Quelle oder im Profil und plane neu. Ändert sich die Quelle nach plan, stoppt der Lauf (NOVA_IMPORT_RUN_SOURCE_CHANGED): neuen Lauf planen.
  • Ein neuer Lauf nach rollback funktioniert mit demselben id-key; status zeigt die neue id_epoch. Ändere den Schlüssel dafür nie.
  • Eine Anlage, die während import auf einem Node scheiterte, legt der nächste import erneut an; behebe vorher die Ursache auf dem Node.
  • NOVA_IMPORT_DATABASE_ENGINE_INVENTORY_STALE (Stufe engines) heißt: kein Datenbank-Node hat innerhalb von 2 Minuten geantwortet. Prüfe die Node Agents und systemctl status nova-controlpanel-operations-database-engine-inventory.timer.
  • Importierte Ressourcen bleiben bis zum Cutover inaktiv (Konten deaktiviert, nichts ausgerollt). Prüfe Websites, Postfächer und Datenbanken direkt auf den Nova-Nodes mit curl --resolve sowie IMAP- und SQL-Logins, bevor du den Cutover planst.
  • Probe den Ablauf auf Testservern: import, verify, rollback, ein zweiter import derselben Quelle (gleiches Ergebnis), dann ein vollständiger Cutover, Fallback und zweiter Cutover.

3. Cutover (P2, Wartungsfenster)

3.1 Quelle einfrieren

Für die umziehenden Mandanten auf den Quellservern: Panel-Änderungen stoppen, Server-Cronjobs stoppen, Mailzustellung am bisherigen MX anhalten, Datenbank- und Web-Inhalts-Writes stoppen. cutover verlangt deine Bestätigung (--legacy-frozen) und weist das Einfrieren dann an der Quelle nach (Snapshot und Änderungsprotokoll unverändert).

3.2 Cutover ausführen

sh
$NI cutover --run "$RUN" --legacy-frozen     # wiederholen, solange Exit 75

Der Cutover führt das letzte Delta und die Prüfung aus. Bei IP-Übernahme wartet er anschließend bei address_takeover mit Exit 75 und gibt address_takeover.operator aus: die genauen check- und swap-Befehle, die Reihenfolge und die erwartete Ausfallzeit.

Lehnt ein Node während der Aktivierung eine Anlage ab (z. B. ein Shell-Konto, dessen Jailkit-Einrichtung scheitert), endet der Aufruf mit Exit 1 und dem Code des Nodes; error.failures listet jede abgelehnte Ressource mit eigenem Code. Behebe die Ursache und führe denselben cutover erneut aus – er legt die abgelehnten Ressourcen neu an, nichts doppelt. rollback ist ab Beginn einer Aktivierung nicht mehr möglich; der Weg ist dann dieser Wiederholungsversuch oder cutover --abort (siehe Entscheidungstabelle).

3.3 Primary IPs tauschen (nur bei Übernahme)

Pro Quellserver von deiner Workstation aus:

sh
python3 tools/import/Invoke-NovaHetznerPrimaryIpSwap.py swap --legacy <legacy> --target <nova-node> \
    --state ~/nova-import/swap-<server>.json --confirm swap:<legacy_id>:<target_id>

Schalte den Quellserver danach in der Hetzner-Konsole ohne öffentliche IP wieder ein, lass seine Dienste gestoppt und führe den Cutover erneut aus, bis er mit 0 endet. Details unter Hetzner-Primary-IP-Übernahme.

3.4 DNS und MX umstellen (Neuzuordnung und DNS-Umzug)

Der abschließende Switch-Bericht listet die Aktionen: SET_DELEGATION_NS_AND_GLUE, SET_MX_AND_ADDRESS_RECORDS (entfällt, wenn alle Server übernommen wurden), LEGACY_MX_RELAYS_TO_NOVA, KEEP_LEGACY_FROZEN und COMMIT_OR_FALLBACK_BEFORE_WINDOW_ENDS. Das Weiterleiten verirrter Mails vom bisherigen MX an Nova richtest du auf dem bisherigen MX ein.

3.5 Erwartete Ausfallzeit

FallWebsitesMailPanel für Kunden
Übernahmevom Herunterfahren in 3.3 bis zur Aktivierung: der Neustart (typisch 2 bis 5 Minuten) plus Aktivierung des Bestandskein Verlust: Absender wiederholen, der bisherige MX hält die Zustellung während des Einfrierens aneingefroren ab 3.1, bis das Fenster öffnet
Neuzuordnung / DNS-Wechselkeine für Lesezugriffe; Inhalts-Writes eingefroren ab 3.1; Besucher wechseln mit der TTLwährend des Einfrierens am bisherigen MX gehalten, dann an Nova weitergeleitetwie oben

Die Werte sind Schätzungen – miss sie bei deiner Generalprobe.

4. Prüfungen nach dem Cutover

PrüfungBefehlErwartet
Websites auf behaltenen IPscurl --resolve site.example.org:443:<ip> https://site.example.org/die Site, ausgeliefert vom Nova-Node
Bisherige Pfadeauf dem Web-Node: readlink /var/www/clients/client<N>/web<N> /var/www/<domain>; ls /var/www/clients/client<N>/web<N>/private/beide zeigen auf /var/www/nova-controlpanel/wst_<ref>; private/ ist umgezogen
Nameserverdig @<nova ns> <zone> SOA, dig <zone> NSNova antwortet mit einer Seriennummer über der bisherigen; neu zugeordnete Einträge tragen die neue Adresse
PTRdig -x <mail ip>bei Übernahme unverändert; bei Neuzuordnung der Mail-Hostname
MX und SMTPdig <domain> MX; SMTP-Sitzung zur MX-AdresseBanner des Nova-Mail-Nodes
Ausgehende Absenderadresseauf dem Mail-Node: postconf smtp_bind_address smtp_bind_address6 inet_interfacesdie bisherige Mail-Adresse (Übernahme)
SPF, DKIM, DMARCTestmail an ein externes Postfach; Authentication-Results lesenspf=pass, dkim=pass mit dem bisherigen Selector, dmarc=pass
DKIM-Eintragdig TXT <selector>._domainkey.<domain>derselbe Schlüssel wie vorher
PostfachIMAP-Login eines umgezogenen Benutzers; neue Mail kommt anfunktioniert
Loginsein umgezogener Kunde und Reseller melden sich im Panel anfunktioniert; wer vorher einen zweiten Faktor nutzte, richtet MFA neu ein
Umzugsstatus$NI status --run "$RUN"Phase fallback_window, keine last_errors

5. Fallback-Fenster und Entscheidung

Beobachte den Bestand während des Fensters. status warnt vor dem Ende; automatisch wird nichts committed.

Fallback (P3 zurück zur Quelle)

  1. Lass den bisherigen MX die Mail der umgezogenen Domains halten und nicht mehr an Nova weiterleiten.
  2. $NI fallback --run "$RUN" --legacy-mx-holding: Nova friert ein, gibt die Mail-Absenderadresse frei, leitet Mail an den bisherigen MX weiter, führt das Rück-Delta aus (Web-Roots und private/, Maildirs, Datenbanken; die Links /var/www/clients/clientN/webN werden entfernt) und schreibt Passwörter und DNS zurück. Danach wartet er bei address_release (Exit 75) mit dem swap-back-Befehl.
  3. Nur bei Übernahme: python3 tools/import/Invoke-NovaHetznerPrimaryIpSwap.py swap-back --state ~/nova-import/swap-<server>.json --confirm swap-back:<legacy_id>:<target_id>.
  4. Führe denselben fallback erneut aus, bis er mit 0 endet, stelle DNS/MX laut Bericht zurück und starte dann die Dienste der Quelle (START_LEGACY_SERVICES_AFTER_SWITCH). Nova leitet weiter, bis die TTLs abgelaufen sind.

Hinweis: Panel-Passwörter, die Nova als Argon2 gespeichert hat, lassen sich nicht zurückschreiben (PASSWORD_RESET_ON_LEGACY im Bericht). Setze sie im bisherigen Panel zurück.

Commit (P4)

sh
$NI commit --run "$RUN" --confirm-irreversible

Danach räumst du auf: Import-Capabilities und SSH-Schlüsseldateien von den Nodes entfernen; Source-Gate, Import-Konten und Fallback-Writer von den Quellservern entfernen; die Hetzner-Token-Datei löschen; die Quellserver außer Betrieb nehmen. Bei Übernahme gehören die bisherigen Primary IPs nun dem Nova-Node; dessen ursprüngliche Primary IPs bleiben unzugewiesen (und kostenpflichtig), bis du sie löschst.

6. Entscheidungstabelle für den Rückweg

SituationPhaseEntscheidungWie
preflight oder plan lehnt abvor P1Quelle oder Profil korrigierenneu planen mit neuer Lauf-ID
import, verify oder delta scheitert wiederholtP1bei der Quelle bleibenrollback --run RUN (entfernt alles, was der Lauf auf Nova angelegt hat), korrigieren, neuen Lauf planen
Nachweis des Einfrierens scheitert (NOVA_IMPORT_LEGACY_NOT_FROZEN, NOVA_IMPORT_RUN_SOURCE_CHANGED)P2den Schreiber auf der Quelle findencutover --abort, Einfrieren aufheben, untersuchen
Letztes Delta oder Prüfung scheitert, kein Tausch erfolgtP2abbrechencutover --run RUN --abort, Einfrieren aufheben; die Quelle liefert weiter aus
Tausch erfolgt, Aktivierung scheitert vor Öffnen des Fensters, Ursache schnell behebbarP2wiederholenUrsache auf dem Node beheben, denselben cutover erneut ausführen
Tausch erfolgt, Aktivierung scheitert, keine schnelle LösungP2abbrechen und zurücktauschencutover --abort gibt den Rücktausch aus; swap-back; auf dem Mail-Node php /opt/nova-controlpanel-node-agent/apps/node-agent/bin/configure-mail-source-address.php --clear, falls mail_source erfolgt war; Quelle starten. Später erneut einfrieren und cutover auf demselben Ziel
Prüfungen nach dem Cutover scheitern, keine schnelle LösungP3FallbackAbschnitt 5, „Fallback“
Probleme nach Ende des Fensters oder nach CommitP4kein Rückwegauf Nova vorwärts beheben oder aus eigenen Backups wiederherstellen
Alle Prüfungen bestanden, Fenster lange genug beobachtetP3CommitAbschnitt 5, „Commit“

rollback wird abgelehnt, sobald irgendein Cutover-Versuch eine Aktivierung begonnen hat – auch nach Abbruch oder Fallback (NOVA_IMPORT_ROLLBACK_AFTER_CUTOVER). Ab dann ist fallback der Rückweg.

Hetzner-Primary-IP-Übernahme

Liegen deine Quellserver in der Hetzner Cloud, kannst du ohne Adressänderung umziehen: A-Einträge, MX-Ziele, PTR, SPF und die Absender-Reputation bleiben an der Primary IPv4 und IPv6 des Quellservers. Beim Cutover wandern diese Primary IPs auf den Nova-Node, beim Fallback zurück.

Wichtig: Nova erhält dein Hetzner-Token nie. Du tauschst die IPs selbst (Hetzner-Konsole, hcloud oder das Hilfsskript unten mit deinem eigenen Token); Nova prüft nur anhand der Adressmeldung des Nodes, ob das Ergebnis stimmt, bevor es etwas aktiviert.

Voraussetzungen (Tage vorher)

Das Importprofil plant jede Adresse pro Quellserver (source.servers[].addresses):

json
"addresses": {"hetzner": {"legacy_server": 4711, "target_server": 4712, "network": 1234567},
              "plan": [{"address": "203.0.113.10", "plan": "takeover", "node_id": "node_..."},
                       {"address": "2001:db8:1::1", "plan": "takeover", "node_id": "node_..."},
                       {"address": "203.0.113.11", "plan": "remap:198.51.100.11"}]}

takeover gilt für die Primary IPv4 (und die IPv6-Adresse, die der Server aus seinem /64 nutzt), remap: für jede andere Adresse. hetzner enthält die numerischen Hetzner-Server-IDs; der Cutover gibt damit die genauen Befehle aus. addresses.operator_checks im Plan-Bericht wiederholt die Prüfungen.

  • Der übernehmende Mail-Node hat mail.source-address.configure.v1 in NOVA_AGENT_WRITE_CAPABILITIES.
  • Quellserver und Nova-Node liegen am selben Hetzner-Standort und im selben privaten Netz. Das private Netz trägt nach dem Tausch jede Übertragung: Trage im Profil als SSH-Host (source.servers[].ssh.host) und als Datenbank-Host die private Adresse des Quellservers ein.
  • Primary IPv4 und IPv6 des Quellservers existieren, sind ihm zugewiesen und haben auto_delete=false; ebenso die eigenen Primary IPs des Nova-Nodes (sie werden für den Fallback unzugewiesen aufbewahrt). Eine Primary IP lässt sich nur bei ausgeschaltetem Server (ent)zuweisen; pro Server eine IPv4 und eine IPv6.
  • PTR-Einträge sind auf den Primary IPs des Quellservers gesetzt (sie gehören zum Primary-IP-Objekt und wandern mit). Bei IPv6 konfiguriert cloud-init <prefix>::1; hat der Mailserver von einer anderen Adresse des /64 gesendet (siehe PTR oder postconf smtp_bind_address6), musst du diese Adresse nach dem Tausch auf dem Nova-Node ergänzen.
  • Das Hilfsskript braucht Python 3.9+ und eine Token-Datei, die nur du lesen kannst:
sh
install -m 0600 /dev/null ~/.config/hcloud-nova-import.token   # dann dein Token hineinkopieren
export HCLOUD_TOKEN_FILE=~/.config/hcloud-nova-import.token     # der Pfad, nie das Token selbst
T=tools/import/Invoke-NovaHetznerPrimaryIpSwap.py
python3 $T check --legacy web1.example.org --target nova-web1 --network 1234567
python3 $T set-auto-delete-off --legacy web1.example.org --target nova-web1
python3 $T check --legacy web1.example.org --target nova-web1 --network 1234567   # muss bestehen

check nennt jede verletzte Voraussetzung (SERVER_LOCATION_MISMATCH, LEGACY_PRIMARY_IPV6_MISSING, …_AUTO_DELETE_ON, …_ASSIGNMENT_UNEXPECTED, …_LOCATION_MISMATCH, PRIVATE_NETWORK_NOT_SHARED) und zeigt Server, Primary IPs mit PTR und die Adressen im gemeinsamen privaten Netz. Hat ein Quellserver kein IPv6, ergänzt du bei jedem Befehl --ipv4-only.

Ablauf beim Cutover

Schritt 1: nova-import cutover --run RUN --legacy-frozen wie gewohnt: Einfrieren, letztes Delta, Prüfung. Danach wartet der Import vor jeder Aktivierung bei address_takeover (Exit 75) und nennt die genauen Befehle pro Quellserver, bis jede übernommene Adresse auf ihrem Nova-Node gemeldet wird.

Schritt 2: Tausch pro Quellserver (Bestätigung swap:<legacy_id>:<target_id> mit den IDs aus check; eine Zustandsdatei pro Paar, bis zum Commit aufbewahren):

sh
python3 $T swap --legacy web1.example.org --target nova-web1 \
    --state ~/nova-import/swap-web1.json --confirm swap:4711:4712

Beim Tausch fahren beide Server geordnet herunter (nach --shutdown-timeout, Standard 300 s, hart), die IPs des Quellservers werden entzogen, die eigenen Primary IPs des Nodes entzogen und aufbewahrt, die bisherigen IPs dem Node zugewiesen und der Node eingeschaltet. Der Quellserver bleibt aus. Ein unterbrochener Tausch wird durch erneutes Ausführen desselben Befehls fortgesetzt; jeder Schritt liest zuerst den aktuellen Zustand und lehnt Unerwartetes ab.

Schritt 3: Schalte den Quellserver in der Hetzner-Konsole ohne öffentliche IP wieder ein: Quelldatenbank und SSH-Transferkonto bleiben über das private Netz erreichbar. Seine Dienste bleiben gestoppt.

Schritt 4: Führe nova-import cutover --run RUN --legacy-frozen erneut aus (solange Exit 75): Er prüft die Adressen auf den Nodes, aktiviert dann alles, entfernt Novas MX-Weiterleitung, bindet den ausgehenden SMTP-Verkehr des Mail-Nodes an die bisherige Mail-Adresse (mail_source) und öffnet das Fallback-Fenster. DNS-, MX- und NS-Einträge brauchen für übernommene Adressen keine Änderung.

Zum Prüfen und Reparieren der Absenderadresse gibt es auf dem Mail-Node configure-mail-source-address.php --ipv4 A --ipv6 B (--show).

Kontrolle

  • swap meldet primary_ips.legacy_ipv4/ipv6.assignee_id = der Node, die eigenen IPs des Nodes unzugewiesen, Quellserver off, Node running.
  • Der Importschritt zeigt jede erwartete Adresse als present.
  • Von außen: curl --resolve site.example.org:443:203.0.113.10 https://site.example.org/, dig -x 203.0.113.10 (PTR unverändert), SMTP-Sitzung zur MX-Adresse (Banner des Nova-Nodes) und eine Testmail an ein externes Postfach (SPF, DKIM, DMARC pass).
  • Auf dem Mail-Node: postconf smtp_bind_address smtp_bind_address6 inet_interfaces.

Ablauf beim Fallback

Schritt 1: nova-import fallback --run RUN --legacy-mx-holding: Nova friert ein, gibt die Absenderadresse frei (mail_source_clear), leitet Mail über das private Netz an den bisherigen MX und spielt Rück-Delta und Rückschreiben auf den Quellserver (noch ohne öffentliche IP). Dann wartet er bei address_release (Exit 75) mit dem swap-back-Befehl.

Schritt 2: Rücktausch (Bestätigung swap-back:<legacy_id>:<target_id>):

sh
python3 $T swap-back --state ~/nova-import/swap-web1.json --confirm swap-back:4711:4712

Beim Rücktausch fahren beide Server herunter, die bisherigen IPs gehen zurück an den Quellserver, die ursprünglichen an den Node, beide starten.

Schritt 3: Führe denselben nova-import fallback erneut aus; er endet, sobald keine übernommene Adresse mehr auf dem Nova-Node liegt.

Schritt 4: Starte die Dienste der Quelle (START_LEGACY_SERVICES_AFTER_SWITCH) und prüfe wie oben. Ein späterer zweiter Cutover führt swap mit derselben Zustandsdatei erneut aus.

Abbruch und Commit

Ein cutover --abort nach dem Tausch gibt den Rücktausch aus: Führe swap-back (und configure-mail-source-address.php --clear, falls mail_source erfolgt war) aus, bevor die Quelle wieder ausliefert.

nova-import commit --confirm-irreversible schließt das Fallback-Fenster. Die bisherigen Primary IPs gehören nun dem Nova-Node (zugewiesen, auto_delete=false); den Quellserver kannst du löschen, ohne sie anzutasten. Lösche anschließend die Token-Datei.