~/ziphub — zsh
ZipHub·~/catalog~/articles~/feed~/sellers~/hire
auth login
[article]category · network

Wie ich von fünf VPS auf einen Dedizierten Server mit Proxmox umgezogen bin

@dignezzz · author17 min read2026-05-05free

TL;DR: TL;DR: Zersplitterte Infrastruktur aus fünf VPS bei drei Anbietern → ein dedizierter Server Ryzen 7 9700X / 64GB ECC / 2x NVMe ZFS-Mirror, darauf Proxmox VE. Darin – VMs/LXCs nach Rollen, NetBird-Mesh statt Portweiterleitung, eine Ausgangs-Node auf einem separaten VPS zur Umgehung von Blockaden und TLS-Terminierung, PBS für Backups. Geld positiv, Kontrolle positiv, Nerven positiv. In diesem Artikel – warum, wie und wohin ich auf dem Weg getreten bin, damit Sie nicht treten.

Wie ich von fünf VPS auf einen Dedizierten Server mit Proxmox umgezogen bin und aufhörte, Hoster zu füttern

TL;DR: Zersplitterte Infrastruktur aus fünf VPS bei drei Anbietern → ein dedizierter Server Ryzen 7 9700X / 64GB ECC / 2x NVMe ZFS-Mirror, darauf Proxmox VE. Darin – VMs/LXCs nach Rollen, NetBird-Mesh statt Portweiterleitung, eine Ausgangs-Node auf einem separaten VPS zur Umgehung von Blockaden und TLS-Terminierung, PBS für Backups. Geld positiv, Kontrolle positiv, Nerven positiv. In diesem Artikel – warum, wie und wohin ich auf dem Weg getreten bin, damit Sie nicht treten.

Warum habe ich das überhaupt angefangen?

Bis Ende letzten Jahres stellte ich fest, dass ich für fünf verschiedene VPS bei drei Hostern bezahlte. Eine – für ein Discourse-Forum, die zweite – für ein Legacy-Forum auf PHP mit MySQL, die dritte – für Abrechnung und Telegram-Bots, die vierte – VPN/Proxy, die fünfte – statische Inhalte und diverse kleine Tools. Und irgendwo dort lief auch n8n, und Vaultwarden, und ein paar Uptime Kumas, die sich gegenseitig rundherum überwachten (wie sollte es anders sein).

Was war schlecht daran:

  • Geld. Insgesamt kamen ungefähr die Kosten für einen anständigen dedizierten Server mit ähnlichen Eigenschaften zusammen (aber dort Shared, hier dediziert).
  • Keine Isolierung. Wenn bei einem Bot-Kunden der Speicher undicht wird – fängt mein Forum an zu stottern. Auf demselben Server. Herrlich.
  • Backups – durch die Hintertür. Auf jeder VPS ein eigener rsync-Cronjob nach S3, überall unterschiedliche Aufbewahrungsfristen, keine Inkrementalität, die Wiederherstellung – ein ganzes Abenteuer.
  • Netzwerk als Salat. SSH über verschiedene Ports, durch Cloudflare Tunnels geleitet, über manuell zwischen zwei VPS eingerichtetes WireGuard, mit Konfigurationen an drei Stellen. Jedes Mal, wenn ich eine neue Maschine hinzufügte – verging ein Tag für die Integration.
  • Update-Apokalypse. Auf fünf verschiedenen Ubuntu/Debian-Versionen mit unterschiedlichen Versionen und Paketen mussten Upgrades manuell durchgeführt werden und niemals gleichzeitig. Alle sechs Monate habe ich heroisch die Quest „alles aktualisieren, nichts kaputt machen“ bestanden.
  • CPU und RAM im Leerlauf. Jede VPS ist für den „Peak“ gebucht, aber im Durchschnitt nur zu 15-20% ausgelastet. Geld fliegt zum Fenster hinaus.

Kurz gesagt – ein klassischer Fall, in dem die Infrastruktur nach dem Prinzip wuchs: „Oh, ich brauche noch einen Dienst – ich kaufe eine VPS“. Es war an der Zeit, das alles zu konsolidieren.

Was ich gewählt habe und warum

Hardware

Ich habe einen dedizierten Server genommen:

  • CPU: AMD Ryzen 7 9700X (8 Kerne / 16 Threads, Zen 5)
  • RAM: 64 GB DDR5 ECC 5200 MHz
  • Festplatten: 2x NVMe 512 GB Enterprise → ZFS-Mirror
  • Netzwerk: 1 Gbps unmetered
  • Preis: ungefähr wie zwei mittlere VPS beim selben OVH

Hypervisor

Proxmox VE 9.x. Alternativen habe ich betrachtet, aber kurz:

  • Nacktes Docker – keine Isolierung, keine normalen Snapshots, Backup nur über Compose-Mittel, Festplatte – eine einzige Müllhalde.
  • VMware ESXi – nicht mehr kostenlos, die Broadcom-Lizenz ist großartig, danke.
  • XCP-ng – in Ordnung, aber persönlich kenne ich Proxmox auswendig und die Community ist lebendiger.
  • Kubernetes – nein, ich bin eine einzelne Person, ich muss keinen Jumbo-Jet bauen, um zum Laden zu fahren.

Proxmox gibt mir alles, was ich brauche: KVM-Virtualmaschinen für alles, wo Isolierung benötigt wird (Docker, spezifische Kernel, verschiedene Distributionen); LXC-Container dort, wo Ressourcen gespart werden können (DBs, Monitoring, kleine Dienste); integriertes PBS für Backups; ZFS out-of-the-box; Snapshots; Live-Migration (wenn jemals ein zweiter dedizierter Server erscheint); eine Weboberfläche, auf der man mit der Maus klicken kann, wenn man keine Lust hat, im Terminal zu tippen.

Architektur: Übersichtsschema

Zuerst zeige ich, was daraus geworden ist, dann erzähle ich, wie ich dorthin gelangt bin.

Die Logik ist folgende:

  1. Auf dem dedizierten Server läuft Proxmox. Er hat die öffentliche IP Y.Y.Y.Y auf vmbr0.
  2. Innen wurde ein zweiter Bridge vmbr1 mit einem privaten Netzwerk 10.10.10.0/24 erstellt. Das ist das Äquivalent eines „internen LAN“ – alle VMs und LXCs hängen dort, nach außen über NAT vom Host, sie haben keine eingehenden öffentlichen Ports.
  3. Auf dem Host selbst laufen nur drei Dinge: Caddy (interner Reverse Proxy), nftables (Firewall) und NetBird (VPN-Agent). Kein Docker auf dem Host, keine Anwendungslogik auf dem Host. Der Host ist eine heilige Kuh, seine Aufgabe ist es, ein Hypervisor zu sein und nicht abzustürzen.
  4. Die virtuellen Maschinen sind nach Rollen aufgeteilt: eine für das Forum, eine für Hintergrunddienste und Bots, eine für Dokploy mit benutzerdefinierten Anwendungen usw. Zwischen ihnen gibt es direkte Sichtbarkeit über 10.10.10.x, nach außen – nur über den Host-Caddy.
  5. LXC-Container – für alles „Leichte“: Postgres-Instanzen verschiedener Versionen, Proxmox Backup Server, Monitoring-Dashboards. Sie sind ebenfalls in vmbr1.
  6. Eine separate Ausgangs-Node („Peer Caddy“) – das ist eine kleine VPS bei einem anderen Anbieter, in einem für mein Publikum angemessenen Netzwerk. Darauf läuft Caddy, der TLS (Let's Encrypt) terminiert und HTTP über das NetBird-Mesh in den dedizierten Server weiterleitet. Die DNS aller öffentlichen Domains zeigen auf diesen VPS, nicht auf den dedizierten Server.
  7. NetBird Mesh verbindet den dedizierten Server, die Ausgangs-Node und meinen Laptop (sowie ein paar alte Server, von denen noch nicht alles umgezogen ist). Das ist WireGuard über SSO-Authentifizierung, ohne offene Ports nach außen.

Warum eine solche Isolierung über die Ausgangs-Node – das ist ein separates Thema, zu dem ich noch zurückkommen werde.

Proxmox-Installation: Wie ich es eingerichtet habe

Ich habe den dedizierten Server bestellt, beim ersten Boot Proxmox über den Rescue-Modus/KVM installiert (OVH hat beides – mir ist KVM lieber, man sieht den gesamten Prozess). Bei der Installation:

  • ZFS RAID1 auf beide NVMe (ashift=12, compression=lz4 sofort aktiviert)
  • Standard-Partitionierung, Proxmox erstellt selbst rpool/ROOT, rpool/data, swap

Danach – grundlegende Bereinigung nach der Installation. Das ist ein Muss, ohne das Proxmox Abonnementfehler ausgibt und nicht optimal funktioniert.

bash
1# 1. Enterprise-Repository entfernen (kein Abonnement)
2sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list
3echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
4  > /etc/apt/sources.list.d/pve-no-subscription.list
5apt update && apt full-upgrade -y
6
7# 2. Nag-Fenster "Kein gültiges Abonnement" entfernen
8sed -Ezi.bak "s/(Ext\.Msg\.show\(\{[^}]+?title: gettext\('No valid sub)/void\(\{ \/\/\1/g" \
9  /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
10systemctl restart pveproxy
11
12# 3. Hostname / Zeitzone
13hostnamectl set-hostname dedik-waw
14timedatectl set-timezone Europe/Warsaw

SSH-Härtung

Sofort. Nicht „später“. Später kommt niemals.

bash
1cat > /etc/ssh/sshd_config.d/hardening.conf << 'EOF'
2Port 22222
3PermitRootLogin prohibit-password
4PasswordAuthentication no
5MaxAuthTries 3
6ClientAliveInterval 300
7ClientAliveCountMax 2
8EOF
9
10mkdir -p ~/.ssh
11echo "ssh-ed25519 AAAA... your-key" >> ~/.ssh/authorized_keys
12chmod 600 ~/.ssh/authorized_keys
13systemctl restart sshd

Port 22 in der Firewall geschlossen, nur der nicht standardmäßige geöffnet. Das ist keine Sicherheit durch Obskurität, sondern eine Reduzierung des Hintergrundrauschens in den Logs – auf Port 22 kommen in der ersten Stunde 50.000 Brute-Force-Versuche an, in den Logs sieht man nichts. Auf 22222 – Stille, und man kann Anomalien wirklich überwachen.

ZFS – Konfiguration für NVMe und 64 GB RAM

ZFS möchte standardmäßig die Hälfte des RAMs für ARC verbrauchen. Das ist normal für einen Dateiserver und absolut kontraproduktiv für einen Hypervisor, wo der Speicher für virtuelle Maschinen benötigt wird. Wir reduzieren den ARC auf vernünftige 16 GB:

bash
1cat > /etc/modprobe.d/zfs.conf << 'EOF'
2options zfs zfs_arc_max=17179869184
3options zfs zfs_arc_min=4294967296
4EOF
5update-initramfs -u

Speicherverteilung meinerseits:

  • ~16 GB – ZFS ARC
  • ~44 GB – VM/LXC
  • ~4 GB – System, Puffer, Overhead

Nützliche Einstellungen des Pools selbst:

bash
1zfs set compression=lz4 rpool
2zfs set atime=off rpool
3zfs set xattr=sa rpool
4zfs set dnodesize=auto rpool
5zfs set relatime=on rpool
6
7zfs set recordsize=64K rpool/data    # für VM-Festplatten
8zfs set sync=standard rpool/data
9
10zpool set autotrim=on rpool          # entscheidend für NVMe

compression=lz4 spart 20-30% Platz fast kostenlos für die CPU. recordsize=64K für VM-Blockgeräte – der beste Kompromiss zwischen Leistung und Schreibamplifikation.

Internes Netzwerk und Firewall

Ich habe eine zweite Bridge für das private Netzwerk mit NAT nach außen erstellt:

1auto vmbr1
2iface vmbr1 inet static
3    address 10.10.10.1/24
4    bridge-ports none
5    bridge-stp off
6    bridge-fd 0
7    post-up   echo 1 > /proc/sys/net/ipv4/ip_forward
8    post-up   iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o vmbr0 -j MASQUERADE
9    post-down iptables -t nat -D POSTROUTING -s 10.10.10.0/24 -o vmbr0 -j MASQUERADE

Jetzt erhält jede VM/LXC in der Bridge vmbr1 eine lokale Adresse 10.10.10.x und geht über den Host ins Internet, ist aber von außen nicht sichtbar.

nftables mit policy drop. Nur das ist offen, was offen sein muss, der Rest wird still gedroppt. Minimale Konfiguration:

nft
1table inet filter {
2    chain input {
3        type filter hook input priority 0; policy drop;
4
5        iif lo accept
6        ct state established,related accept
7        ip protocol icmp accept
8        ip6 nexthdr icmpv6 accept
9
10        tcp dport 22222 accept           # SSH
11        tcp dport 8006 accept            # Proxmox UI (nur aus NetBird, siehe unten)
12        tcp dport { 80, 443, 8080 } accept   # Caddy
13        udp dport 29899 accept           # NetBird
14
15        iif vmbr1 accept                 # internes Netzwerk – alles erlaubt
16        iif wt0 accept                   # NetBird-Schnittstelle – ebenfalls vertrauenswürdig
17    }
18
19    chain forward {
20        type filter hook forward priority 0; policy drop;
21        iif vmbr1 oif vmbr0 accept
22        ct state established,related accept
23    }
24}

Der Zugriff auf die Proxmox UI (Port 8006) ist physisch vom Internet über CF/Firewall gesperrt, und ich greife nur über NetBird darauf zu. Über die öffentliche IP reagiert die Oberfläche überhaupt nicht. Dasselbe gilt für PBS UI, Dashboards und alles Administrative – nur über das Mesh. Von außen sind nur die wirklich öffentlichen Ports der Dienste geöffnet.

NetBird Mesh: Warum nicht klassisches WireGuard?

Ich habe lange Zeit normales WireGuard benutzt, und das war in Ordnung, bis es mehr als drei Server gab. Danach wurde die Verwaltung von Konfigurationen zur Qual: Jede Peer-Hinzufügung wird zu „Konfiguration auf allen Nodes aktualisieren, allowed_ips nicht vergessen, Dämon neu starten, prüfen, ob die Route zum anderen Peer nicht kaputtgegangen ist“.

NetBird ist eine Management-Ebene über WireGuard. Technisch gesehen ist darunter immer noch dein übliches wg, aber:

  • Nodes finden sich über einen Signalisierungsserver, NAT-Traversal ist automatisch
  • Authentifizierung über SSO (bei mir – über Authentik), ohne Schlüsselteilung
  • Der Zugriff wird über die Weboberfläche über Richtlinien gesteuert, man kann Maschinen-Gruppen und Regeln zwischen ihnen erstellen
  • Integrierter SSH-Proxy: Man kann über ssh user@machine.netbird.cloud zugreifen und SSO-Authentifizierung erhalten (mit Genehmigung in Authentik). Auf der Node muss nicht einmal Port 22 nach außen geöffnet werden.

Ich habe den NetBird-Agenten installiert:

  • Auf dem Proxmox-Host
  • Auf jeder VM, die für den Administrator oder andere VMs sichtbar sein soll
  • Auf der Ausgangs-Node
  • Auf meinem Laptop

Das Ergebnis: Ich greife von meinem Laptop auf jede Maschine mit einem einzigen Befehl zu, ohne Portweiterleitung, ohne Firewall-Lücken, ohne zusätzliche Bastions-Hosts. Und es gibt kein offenes SSH auf einer der privaten VMs – sie sind alle nur im vmbr1 plus dem wt0-Mesh.

Virtuelle Maschinen vs. Container: Wie ich das für mich gelöst habe

Eine einfache Regel, die fast immer funktioniert:

WannWas nehmen
Docker-WorkloadsVM (Docker in LXC kann launisch sein, besonders mit overlay2)
Datenbanken ohne DockerLXC (leichter, schneller, direkter Festplattenzugriff über ZFS)
Systemdienste (PBS, Monitoring, kleine Tools)LXC
Anwendungen, die einen eigenen Kernel / Netzwerk-Tricks benötigenVM
VPN-Knoten (WG, AmneziaWG)LXC (minimaler Overhead)

Am Ende kam es bei mir ungefähr so heraus:

Virtuelle Maschinen (KVM):

  • forum – Discourse im Standard-Launcher-Container. Docker darin.
  • services – viele Kleinigkeiten: Webhook-Bots, KI-Bot, Abrechnungspanel, Vaultwarden, Uptime Kuma. Alles in Docker Compose, pro Ordner pro Dienst.
  • legacy – altes PHP-Forum (IPS) mit nativem PHP-FPM und Caddy auf der VM. Wollte es nicht in Docker verpacken – zu viel Legacy, einfacher, es so zu lassen, wie es war.
  • dokploy – Docker Swarm + Dokploy für benutzerdefinierte Anwendungen (Next.js, Postgres, Redis), Deployment über Git.

LXC:

  • pbs – Proxmox Backup Server (siehe unten).
  • pg16, pg17, pg18 – drei separate Postgres-Instanzen verschiedener Major-Versionen. Grund: Unterschiedliche Anwendungen benötigen unterschiedliche Major-Versionen, und ich möchte nicht jede einzeln hoch- und runterskalieren.
  • dashboards – ein Container mit Homarr (Startseite), Uptime Kuma und Beszel Hub. Alle drei in einem Compose, weil sie logisch dasselbe tun – beobachten.

Jede VM erhält eine statische IP in 10.10.10.x (nach Bereichen, so ist es einfacher zu suchen), 2-8 vCPU-Kerne und 2-8 GB RAM je nach Last. Kerne können sicher überprovisioniert werden – ich habe insgesamt mehr Kerne für VMs zugewiesen, als physisch vorhanden sind, und das funktioniert hervorragend, solange niemand gleichzeitig an die CPU-Grenzen stößt.

Reverse Proxy: Zweistöckiger Caddy

Innerhalb von Proxmox habe ich Caddy auf dem Host installiert, nicht in einer VM. Warum? Weil er die öffentlichen Ports des dedizierten Servers abhören und anhand des Host-Headers zur richtigen VM weiterleiten muss, und dafür eine separate VM mit Portweiterleitung zu erstellen – das ist eine zusätzliche Ebene ohne Nutzen. Caddy ist leicht, in Go geschrieben, systemd-Unit, Konfiguration in einer Datei.

Dieser Host-Caddy hört nur auf Port 8080 HTTP und leitet an Backends über 10.10.10.x weiter. Kein TLS darauf.

caddy
1:8080 {
2    @forum host forum.example.com www.forum.example.com
3    handle @forum {
4        reverse_proxy http://10.10.10.111:80 {
5            header_up X-Forwarded-Proto https
6            header_up X-Real-IP {header.X-Real-IP}
7            header_up Host {host}
8        }
9    }
10
11    @panel host panel.example.com api.panel.example.com
12    handle @panel {
13        reverse_proxy http://10.10.10.111:8081 {
14            header_up X-Forwarded-Proto https
15            header_up X-Real-IP {header.X-Real-IP}
16            header_up Host {host}
17        }
18    }
19
20    @dokploy host app1.example.com app2.example.com
21    handle @dokploy {
22        reverse_proxy http://10.10.10.114:80 {
23            header_up X-Forwarded-Proto https
24            header_up X-Real-IP {header.X-Real-IP}
25            header_up Host {host}
26        }
27    }
28}

Und die TLS-Terminierung und Zertifikate – auf einer separaten Ausgangs-Node, in Docker, nach demselben Prinzip:

caddy
1{
2    email me@example.com
3}
4
5forum.example.com {
6    reverse_proxy http://100.x.y.z:8080 {
7        header_up X-Forwarded-Proto {scheme}
8        header_up X-Forwarded-For {remote_host}
9        header_up X-Real-IP {remote_host}
10        header_up Host {host}
11    }
12}

100.x.y.z – das ist die NetBird-Adresse des dedizierten Servers. Die Anfrage kommt über HTTPS an der Ausgangs-Node an, wird entschlüsselt, geht per HTTP über das NetBird-Mesh zum dedizierten Server, dort matcht der Proxmox Caddy anhand des Hosts und leitet zur internen VM weiter. Zwischen der Ausgangs-Node und dem dedizierten Server ist HTTP per Design – wir vertrauen dem Mesh, kein externer Traffic hier.

Warum überhaupt eine Ausgangs-Node?

Mehrere Gründe:

  1. Umgehung von Blockaden. Ein Teil der OVH-Subnetze wird in Russland gesperrt. Die Ausgangs-Node befindet sich bei einem Anbieter, dessen IP nicht blockiert wird. Die DNS aller öffentlichen Domains zeigen dorthin.
  2. TLS-Terminierung an einem Ort. Nur sie gibt Zertifikate aus, nur ihre IP ist für Let's Encrypt beim Challenge. Das vereinfacht alle DNS-Cleared Checks und ermöglicht es dem dedizierten Server, für 80/443 unsichtbar vom Internet zu sein.
  3. Zusätzliche Isolationsschicht. Wenn jemand anfängt, DDoS-Angriffe durchzuführen – er greift die Ausgangs-Node an, nicht den dedizierten Server. Auf der Ausgangs-Node habe ich minimale Daten, sie neu zu erstellen – 10 Minuten.
  4. Trennung von öffentlichem Frontend und Backend. Ich kann bedenkenlos Caddy-Updates durchführen / auf dem dedizierten Server experimentieren, während die tatsächliche Schnittstelle zum Internet separat und stabil ist.

Ein Nachteil: Jeder Anfrage werden ~5-15 ms durch den zusätzlichen Hop hinzugefügt. Für das Web ist das unmerklich.

Für einige Dienste gibt es auch direkt Caddy auf dem Host unter 80/443 ohne Ausgangs-Node – für die Domains, die ich nicht über den Ausgangspunkt weiterleiten muss (z.B. Status-Endpunkte, Monitoring, Dienste, bei denen DNS sowieso auf den dedizierten Server zeigt). Das funktioniert parallel: Caddy auf dem Host hört sowohl :8080 (intern von der Ausgangs-Node) als auch :80/:443 (direkt aus dem Internet), die Routen sind unterschiedlich.

Migration: Wie ich ohne Tränen und fast ohne Ausfallzeit umgezogen bin

Das war die beängstigendste Phase. Fünf Server, etwa dreißig Dienste, ein Produktionsforum mit aktiven Nutzern, Abrechnung mit aktiven Abonnements, Datenbanken, die auf keinen Fall verloren gehen dürfen.

Strategie – Migration eines Dienstes nach dem anderen, in der Reihenfolge „vom am wenigsten kritischen zum kritischsten“:

  1. Zuerst zogen die kleinen Tools und statischen Websites um (wenn etwas schiefgeht – niemand wird es bemerken).
  2. Dann die Bots und Hintergrunddienste (auch sie verkraften eine Minute Ausfallzeit).
  3. Dann die Datenbanken (mit zuvor überprüfter Wiederherstellung).
  4. Dann die Anwendungen, die auf diese Datenbanken zugreifen.
  5. Ganz am Ende – das Hauptforum mit einer langen finalen Synchronisationsrunde und DNS-Umschaltung.

Migrationskanal

Zuerst habe ich einfach NetBird installiert auf den alten Servern. Das löste sofort zwei Probleme: Ich kann sicher per SSH auf die internen Netzwerke zugreifen, und rsync läuft über WireGuard über NetBird, ohne die Daten im offenen Internet preiszugeben.

Das Arbeitspferd für jeden Dienst war ungefähr so:

bash
1# Vom neuen dedizierten Server, über die NetBird-IP des alten Servers:
2rsync -avzP --delete \
3  -e "ssh -p 5322" \
4  root@100.76.108.210:/var/discourse/shared/standalone/ \
5  /target/discourse/shared/standalone/

Für Datenbanken – Dump + Restore, keine Dateikopie. Kopieren Sie niemals Postgres/MySQL-Dateien „heiß“, das ist ein Ticket in die Hölle.

bash
1# Auf dem alten
2docker exec -it shm-vsem-mysql mysqldump -u root -p shm-vsem | \
3  gzip > /tmp/shm-vsem.sql.gz
4
5# Auf dem neuen, über NetBird
6scp -P 5322 root@100.76.108.210:/tmp/shm-vsem.sql.gz /tmp/
7zcat /tmp/shm-vsem.sql.gz | docker exec -i shm-vsem-mysql mysql -u root -p shm-vsem

Für Discourse – die native discourse backup / discourse restore über ./launcher enter app. Intern sammelt es korrekt den Dump von Postgres + Uploads + Konfigurationen und stellt sie genauso wieder her.

DNS-Umschaltung

Nachdem der Dienst am neuen Ort gestartet und seine Funktionalität über die interne Adresse überprüft wurde – habe ich beide Installationen für 5-15 Minuten parallel laufen lassen, um die Datensynchronität zu beobachten, und dann das DNS auf den neuen Server umgeschaltet. Die TTL der Einträge hatte ich vorsorglich einen Tag vor der Umschaltung auf 60-300 Sekunden reduziert.

Für Discourse, bei dem aktive Benutzer ständig posten, habe ich es so gemacht:

  1. Alles am neuen Ort eingerichtet.
  2. Validierung durchgeführt (Login, Posten, Upload von Dateien nach S3, Suche).
  3. Auf der alten Installation den read_only_mode aktiviert (Discourse kann das out-of-the-box).
  4. Einen finalen rsync der Uploads + einen finalen Datenbank-Dump erstellt.
  5. Auf dem neuen gestartet, Read-only deaktiviert.
  6. DNS umgeschaltet.
  7. Auf der alten Installation nichts mehr angefasst für eine Woche – für den Fall der Fälle.

Die tatsächliche Ausfallzeit für Benutzer – etwa zwei Minuten pro Dienst.

Überraschungen unterwegs

  • Berechtigungen bei pct restore. LXC unter unprivileged 1 bildet UIDs mit einem Offset von 100000 ab. Wenn auf dem alten Server Dateien unter UID 1000 waren – auf dem neuen sind sie unter UID 101000, und die Anwendung findet sie nicht. Löst sich entweder durch chown nach dem Restore oder --unprivileged 0, wenn Sie die Risiken verstehen.
  • Docker IPv6 in OVH-VMs startete manchmal fehlerhaft – behoben durch Deaktivieren von IPv6 im Docker-Daemon ("ipv6": false).
  • Zeit auf VMs. Mehrfach Abweichungen der Systemzeit zwischen Host und VM festgestellt, was TLS und Signaturen brach. Heilmittel – systemd-timesyncd oder chrony auf jeder VM und host.use-time im Proxmox-Agenten.
  • Discourse-Migration zwischen zwei Versionen. Wenn Discourse auf dem alten Server neuer ist als das, was Sie auf dem neuen installiert haben – schlägt restore fehl. Die Version muss auf der neuen Installation gleich oder neuer sein.
  • Image Registry für Dokploy. Als ich die Metadaten von Dokploy über einen dokploy-postgres-Dump übertrug, blieben in der Datenbank Links zum alten lokalen Registry mit der alten IP. Dienste fielen beim Start mit No such image aus, weil sie auf 100.76.117.115:5000 zugriffen. Lösung – jedes Anwendung in Dokploy UI neu bauen; der lokale Build legt das Image bereits auf dem neuen Server ab.

Backups: PBS innerhalb desselben dedizierten Servers + Offsite

Hier gab es eine separate philosophische Diskussion mit mir selbst: soll man auf derselben Hardware sichern oder nicht? Antwort: Sowohl ja als auch nein.

Level 1 – ZFS-Snapshots auf demselben Pool. Sie sind fast kostenlos (Copy-on-Write), werden sofort erstellt, sofort wiederhergestellt. Ich halte automatische Snapshots von rpool/data alle 15 Minuten mit einer Aufbewahrungsfrist von 24 Stunden und tägliche mit einer Aufbewahrungsfrist von einer Woche. Verwendet wird zfs-auto-snapshot. Schutz vor „Oh, ich habe gerade die Produktions-DB gelöscht“:

bash
1apt install zfs-auto-snapshot
2# Danach erstellt es selbst über Cron frequent/hourly/daily/weekly/monthly

Level 2 – Proxmox Backup Server in einem separaten LXC auf demselben Host. Das sind vollwertige inkrementelle Backups von VM/LXC über vzdump, dedupliziert auf Chunk-Ebene. PBS speichert Snapshots in einem separaten ZFS-Datensatz, sieht sie als Repository, jede VM hat ihre eigene Kette von inkrementellen Backups.

Konfiguration in Proxmox: Datacenter → Backup → Add. Zeitplan: Jeden Tag um 4 Uhr morgens, Aufbewahrungsfrist keep-daily=7 keep-weekly=4 keep-monthly=3. Das alles per Maus.

Die Platzierung von PBS in LXC auf demselben Host – ein Kompromiss. Vorteil: Keine separate Hardware benötigt, Backup läuft über localhost, Geschwindigkeit wie bei lokalem SSD, Retention/Dedup funktioniert perfekt. Nachteil: Wenn der dedizierte Server komplett ausfällt – fallen auch die Backups aus. Deshalb gibt es...

Level 3 – Synchronisation des PBS-Repos nach Offsite S3. PBS unterstützt sync job in S3-kompatiblem Speicher. Jede Nacht übertrage ich die Snapshots in einen separaten Bucket in Selectel S3 (kann jeder sein – Backblaze, Wasabi, Cloudflare R2). Die Aufbewahrungsfrist dort – zwei Wochen, denn mehr ist nicht nötig: für die Langzeitaufbewahrung gibt es das lokale PBS, und Offsite ist eine Versicherung gegen „ganzes Rechenzentrum abgebrannt“.

Level 4 – Anwendungsdaten separat. Discourse selbst erstellt seine Backups und legt sie in S3 ab (das ist eine native Funktion). So habe ich selbst dann noch konsistente Discourse-Dumps in deren Cloud, wenn PBS und sogar der gesamte dedizierte Server ausfällt.

bash
1# Wiederherstellung einer ganzen VM aus PBS – buchstäblich ein Befehl:
2pvesm list backup-pbs    # um zu sehen, was vorhanden ist
3qmrestore backup-pbs:backup/vzdump-qemu-201-2026_04_06-04_00_03.vma.zst 999 \
4  --storage local-zfs
5# und nach einer Minute läuft auf Ihrer VMID 999 eine Kopie des Forums-VM vom Zeitpunkt 4 Uhr morgens.

Das ist ein kritischer Punkt: Backups, die Sie nicht wiederherzustellen versucht haben – sind keine Backups. Ich mache einmal im Monat einen Test-Restore einer zufälligen VM auf eine leere VMID, prüfe, ob sie startet, ob die Anwendung darin funktioniert, und lösche sie. Langweilig, aber einmal fand ich so heraus, dass einer der Cronjobs nach /tmp/... schreibt, was beim Backup übersprungen wird, und die Anwendung nach dem Restore einen manuellen Schritt erfordert.

Monitoring

Ich sammle keine Komplexe wie Prometheus + Grafana + Alertmanager + Loki, wenn ich 15 Maschinen habe. Deshalb habe ich mich für drei leichte Tools in einem LXC entschieden:

  • Beszel – sammelt Metriken von Agenten auf jeder VM (CPU, RAM, Festplatten, Netzwerkschnittstellen, Docker-Container). Der Hub läuft in LXC, die Agenten – auf jeder VM/LXC über systemd-Unit. Auth bei Beszel ist eigener, über PocketBase, was manchmal unpraktisch ist (siehe unten wegen Stolperstein), aber im Allgemeinen funktioniert.
  • Uptime Kuma – Checks über HTTP/HTTPS/Ping/TCP. Ich habe dort alles öffentliche (Domains), alle internen Dienste (über NetBird) und Pings zu allen Mesh-Nodes eingetragen. Alarme – in Telegram.
  • Homarr – Startseite mit Links zu allen Admin-Oberflächen. Um nicht vergessen zu müssen, über welchen Port die PBS UI erreichbar ist, über welchen Dokploy, über welchen Vaultwarden. Ich öffne einfach Homarr und klicke.

Alle drei in einem Docker Compose, in einem LXC, nur über NetBird erreichbar.

Stolperstein mit Beszel, damit Sie nicht treten: Beszel hat zwei Benutzertabellen in PocketBase – _superusers (für CLI/API) und users (für die Weboberfläche). Wenn Sie das Passwort über die CLI superuser upsert zurücksetzen – setzen Sie nur _superusers zurück, und in der UI melden Sie sich über users an und das Passwort passt nicht. Lässt sich durch einen PATCH-Request an /api/collections/users/records/<id> über die REST-API beheben.

Was ich letztendlich erhalten habe

Nach etwa drei Wochen Migration, als die letzten Dienste stabil liefen, zog ich Bilanz:

ParameterVorherNachher
Server / Rechnungen5 bei 3 Hostern1 dedizierter Server + 1 Mikro-VPS
monatliche Kosten~$X~$X/2
Freie Ressourcen„scheint zu reichen“8 vCPU und 30 GB RAM Reserve
Backupsrsync-Cronjob in S3 auf jeder VPSPBS + Offsite + ZFS-Snapshots
Wiederherstellung„nun, ein Tag irgendwo“2-5 Minuten pro VM aus PBS
SSH-Zugriff5 verschiedene Ports und Schlüsselein NetBird-Mesh mit SSO
Dienstisolierunggemeinsame Müllhaldejede in ihrer eigenen VM/LXC
Snapshot vor Upgrade„bete“qm snapshot 201 pre-update
Weboberfläche für Routineaufgabenwelche Weboberfläche?Proxmox UI

Das Wichtigste, was mir das emotional gegeben hat – ich habe keine Angst mehr, etwas anzufassen. Jede gefährliche Aktion (Upgrade, Migration, Experiment) beginnt jetzt mit einem Snapshot und endet entweder mit einem Commit oder einem Rollback in 30 Sekunden. Ich habe angefangen, öfter Neues auszuprobieren, weil die Kosten für Fehler um eine Größenordnung gesunken sind.

Checkliste, falls Sie es wiederholen möchten

Wenn Sie sich bei sich selbst auf dieselben Symptome wie bei mir ertappen – hier ist eine kurze Checkliste, in welcher Reihenfolge es sinnvoll ist, vorzugehen:

  1. Berechnen Sie, wie viel Sie für alle VPS zusammen bezahlen, und vergleichen Sie es mit den Preisen für dedizierte Server bei OVH/Hetzner/LeaseWeb. Sie werden überrascht sein.
  2. Nehmen Sie ECC RAM, wenn Sie ZFS planen. Sparen Sie nicht.
  3. Nehmen Sie mindestens zwei Festplatten im Spiegel. Eine einzelne Festplatte ist keine Option für den Produktionseinsatz.
  4. Installieren Sie Proxmox über KVM/IPMI, nicht über Rescue + Debootstrap. Weniger Schmerzen.
  5. Sofort SSH-Schlüssel einrichten, nicht standardmäßigen Port verwenden, Passwörter deaktivieren, fail2ban installieren.
  6. Sofort ZFS ARC begrenzen. Standardmäßig frisst er die Hälfte des RAMs.
  7. Erstellen Sie eine zweite Bridge vmbr1 für das private Netzwerk mit NAT. Alle VMs/LXCs dorthin. Keine öffentlichen IPs auf den virtuellen Maschinen.
  8. Richten Sie NetBird (oder Tailscale, oder Headscale) ein, bevor Sie mit der Migration beginnen. Das ist Ihr Migrationskanal und Admin-Zugriff.
  9. Öffnen Sie die Proxmox UI nicht nach außen. Nur über VPN-Mesh.
  10. Eine kleine ausgehende VPS bei einem anderen Anbieter für TLS-Terminierung und Umgehung von Blockaden – nicht zwingend erforderlich, aber sehr nützlich. Die Kosten sind gering, die Vorteile zahlreich.
  11. PBS in separatem LXC + Offsite Sync in S3. Backups müssen automatisch erfolgen.
  12. Machen Sie mindestens einmal im Monat einen Test-Restore. Sonst sind es keine Backups, sondern Dateien zur Selbstberuhigung.
  13. Migrieren Sie einen Dienst nach dem anderen, in aufsteigender Reihenfolge der Kritikalität. Versuchen Sie nicht, „alles am Wochenende zu verlegen“.
  14. Reduzieren Sie die DNS-TTL einen Tag vor der Umschaltung, nicht eine Stunde.
  15. Führen Sie Dokumentation für jeden Dienst: wo die Konfigurationen sind, wie man startet, wie man sichert, wie man wiederherstellt. Nach einem halben Jahr werden Sie sich selbst danken.

Was noch zu tun bleibt

Der Vollständigkeit halber: Die Migration ist bei mir noch nicht zu 100 % abgeschlossen. Übrig bleiben:

  • Ein paar Remnawave-Panels vom alten Server (geplant für nächstes Wochenende).
  • Übertragung von Caddy mit Plugins (dort eine benutzerdefinierte Kompilierung), funktioniert derzeit nur temporär.
  • Alter CrowdSec – wird separat neu installiert, nicht migriert.

Aber alles Kritische (Foren, Abrechnung, Bots, Datenbanken) – ist bereits auf dem dedizierten Server, läuft stabil, wird gesichert und überwacht.

Wenn Sie Fragen zu bestimmten Schritten, Konfigurationen oder Stolpersteinen haben, auf die ich detaillierter getreten bin – schreiben Sie in die Kommentare, ich versuche, sie zu erklären. Besonders interessiert mich zu hören, wie Sie ähnliche Aufgaben auf anderen Hypervisoren (XCP-ng, ESXi oder generell auf nacktem Docker Swarm) lösen – vielleicht übersehe ich etwas.

Viel Erfolg bei der Konsolidierung Ihrer Infrastrukturen. Weniger Rechnungen, mehr Kontrolle – das ist es wert.

~17 min read · scroll to continue ↓

## discussion

$ topics --entity=article0
sign in to start or join a discussion
No discussions yet — start one to break the ice.
↑↓ nav open⌘K palettei install? help