Come sono passato da cinque VPS a un unico server dedicato con Proxmox e ho smesso di alimentare gli hoster
TL;DR: Infrastruttura frammentata da cinque VPS presso tre provider → un server dedicato Ryzen 7 9700X / 64GB ECC / mirror ZFS 2x NVMe, sopra Proxmox VE. All'interno — VM/LXC per ruolo, mesh NetBird invece del port forwarding, nodo di uscita su un VPS separato per aggirare blocchi e terminazione TLS, PBS per i backup. Soldi in attivo, controllo in attivo, nervi in attivo. In questo articolo — perché, come e dove ho inciampato lungo la strada, in modo che tu non inciampi.
Perché ho intrapreso tutto questo
Verso la fine dell'anno scorso, mi sono ritrovato a pagare per cinque VPS differenti presso tre provider. Uno — per il forum Discourse, il secondo — per un forum legacy su PHP con MySQL, il terzo — per il billing e i bot Telegram, il quarto — VPN/proxy, il quinto — statiche e vari piccoli tool. E da qualche parte lì giravano anche n8n e Vaultwarden, e un paio di Uptime Kuma che si monitoravano reciprocamente (come altrimenti).
Cosa c'era di sbagliato:
- Soldi. In totale usciva circa lo stesso importo che costa un server dedicato decente con caratteristiche simili (ma lì era condiviso, qui invece è dedicato).
- Nessuna isolazione. Se la memoria di un bot di un cliente inizia a fuoriuscire, il mio forum inizia a rallentare. Sulla stessa macchina. Fantastico.
- Backup — attraverso il culo. Ogni VPS aveva il suo rsync-cron su S3, retention diversi ovunque, nessuna incrementalità, ripristinare — un'impresa.
- Rete come un insalata. SSH attraverso porte diverse, inoltrate tramite Cloudflare Tunnels, tramite WireGuard sollevato manualmente tra due VPS, con configurazioni in tre posti. Ogni volta che aggiungevo una nuova macchina, ci voleva un giorno per l'integrazione.
- Apocalisse degli aggiornamenti. Su cinque diverse Ubuntu/Debian con versioni e pacchetti diversi, gli aggiornamenti andavano fatti manualmente e mai contemporaneamente. Una volta ogni sei mesi, ero eroicamente eroico nel completare la missione "aggiorna tutto, non rompere nulla".
- CPU e RAM inutilizzate. Ogni VPS era prenotata "al picco", e in media veniva utilizzata al 15-20%. Soldi buttati nel vento.
In generale — un caso classico in cui l'infrastruttura è cresciuta secondo il principio "oh, serve un altro servizio — compro una VPS". Era ora di consolidare tutto questo.
Cosa ho scelto e perché
Hardware
Ho preso un server dedicato:
- CPU: AMD Ryzen 7 9700X (8 core / 16 thread, Zen 5)
- RAM: 64 GB DDR5 ECC 5200 MHz
- Dischi: 2× NVMe 512 GB enterprise → mirror ZFS
- Rete: 1 Gbps unmetered
- Prezzo: circa come due VPS medie presso lo stesso OVH
Hypervisor
Proxmox VE 9.x. Ho considerato le alternative, ma brevemente:
- Docker nudo — nessuna isolazione, nessuno snapshot normale, backup solo tramite compose, disco — un unico calderone.
- VMware ESXi — non più gratuito, la licenza Broadcom è fantastica, grazie.
- XCP-ng — ok, ma personalmente conosco Proxmox a memoria e la sua community è più viva.
- Kubernetes — no, sono una persona sola, non ho bisogno di costruire un aereo per andare al negozio.
Proxmox mi dà tutto ciò di cui ho bisogno: VM KVM per tutto ciò che richiede isolazione (Docker, kernel specifici, diverse distribuzioni); container LXC dove si possono risparmiare risorse (DB, monitoraggio, piccoli servizi); PBS integrato per i backup; ZFS out-of-the-box; snapshot; live-migrazione (se mai apparisse un secondo server dedicato); interfaccia web su cui si può cliccare quando non si ha voglia di scrivere nel terminale.
Architettura: schema generale
Prima mostrerò cosa è venuto fuori, poi spiegherò come ci sono arrivato.
La logica è questa:
- Sul server dedicato vive Proxmox. Gestisce l'IP pubblico
Y.Y.Y.Ysuvmbr0. - All'interno è stato creato un secondo bridge
vmbr1con una rete privata10.10.10.0/24. È un analogo di "LAN interna" — tutte le VM e gli LXC sono lì, verso l'esterno tramite NAT dall'host, non hanno porte pubbliche in entrata. - Sull'host stesso girano solo tre cose:
Caddy(reverse proxy interno),nftables(firewall) eNetBird(agente VPN). Nessun Docker sull'host, nessuna logica applicativa sull'host. L'host è una vacca sacra, il suo compito è essere un hypervisor e non cadere. - Le VM sono divise per ruoli: una per il forum, una per i servizi in background e i bot, una per Dokploy con le applicazioni utente, ecc. Tra di loro c'è visibilità diretta tramite
10.10.10.x, verso l'esterno — solo tramite il Caddy dell'host. - I container LXC — per tutto ciò che è "leggero": istanze Postgres di diverse versioni, Proxmox Backup Server, dashboard di monitoraggio. Anch'essi in
vmbr1. - Un nodo di uscita separato ("Peer Caddy") — è una piccola VPS presso un altro provider, in una rete adeguata per il mio pubblico. Su di esso gira Caddy, che termina TLS (Let's Encrypt) e fa proxy HTTP verso l'interno del server dedicato tramite la mesh NetBird. Il DNS di tutti i domini pubblici punta a questa VPS, non al server dedicato.
- La mesh NetBird collega il server dedicato, il nodo di uscita e il mio laptop (e anche un paio di vecchi server da cui non tutto si è ancora spostato). È WireGuard sopra l'autenticazione SSO, senza porte aperte verso l'esterno.
Perché una tale isolazione tramite nodo di uscita è un discorso a parte, a cui tornerò ancora.
Installazione Proxmox: come l'ho configurato
Ho ordinato il server dedicato, al primo avvio ho installato Proxmox tramite modalità rescue/KVM (OVH ha entrambe — per me è più comodo KVM, si vede tutto il processo). Durante l'installazione:
- ZFS RAID1 su entrambi gli NVMe (
ashift=12, compression=lz4 abilitata subito) - partizionamento di default, Proxmox fa da sé
rpool/ROOT,rpool/data, swap
Successivamente — pulizia di base dopo l'installazione. È un must, senza di esso Proxmox darà errori di sottoscrizione e non funzionerà in modo ottimale.
1# 1. Rimuovere il repository enterprise (nessuna sottoscrizione)
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. Rimuovere la finestra di avviso "No valid subscription"
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 / timezone
13hostnamectl set-hostname dedik-waw
14timedatectl set-timezone Europe/Warsaw
Indurimento SSH
Subito. Non "dopo". Il dopo non arriva mai.
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
Ho chiuso la porta 22 nel firewall, lasciando solo quella non standard. Questa non è sicurezza tramite oscurità, ma riduzione del rumore di fondo nei log — sulla porta 22 nelle prime ore arrivano 50 mila tentativi di brute force, nei log non si vede nulla. Sulla 22222 — silenzio e si possono monitorare davvero le anomalie.
ZFS — configurazione per NVMe e 64GB RAM
ZFS per impostazione predefinita vuole consumare metà della RAM per ARC. Questo va bene per un file server e è assolutamente sconsigliato per un hypervisor, dove la memoria serve alle VM. Riduciamo ARC a un ragionevole 16 GB:
1cat > /etc/modprobe.d/zfs.conf << 'EOF'
2options zfs zfs_arc_max=17179869184
3options zfs zfs_arc_min=4294967296
4EOF
5update-initramfs -u
Distribuzione della mia memoria:
- ~16 GB — ZFS ARC
- ~44 GB — VM/LXC
- ~4 GB — sistema, buffer, overhead
Impostazioni utili del pool stesso:
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 # per dischi VM
8zfs set sync=standard rpool/data
9
10zpool set autotrim=on rpool # critico per NVMe
compression=lz4 risparmia il 20-30% di spazio quasi gratuitamente in termini di CPU. recordsize=64K per i dispositivi a blocchi delle VM è il miglior compromesso tra prestazioni e amplificazione della scrittura.
Rete interna e firewall
Ho creato un secondo bridge per la rete privata con NAT verso l'esterno:
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
Ora ogni VM/LXC nel bridge vmbr1 riceve un indirizzo locale 10.10.10.x e esce su Internet tramite l'host, ma dall'esterno non è visibile.
nftables con policy drop. È aperto solo ciò che deve esserlo, il resto viene silenziosamente scartato. Set minimo:
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 # UI Proxmox (solo da NetBird, vedi sotto)
12 tcp dport { 80, 443, 8080 } accept # Caddy
13 udp dport 29899 accept # NetBird
14
15 iif vmbr1 accept # rete interna — tutto permesso
16 iif wt0 accept # interfaccia NetBird — anch'essa fidata
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}
L'accesso all'UI di Proxmox (porta 8006) è fisicamente chiuso da Internet tramite CF/firewall e vi si accede solo tramite NetBird. Tramite IP pubblico l'interfaccia non risponde affatto. Lo stesso vale per l'UI di PBS, le dashboard e tutto ciò che è amministrativo — solo tramite la mesh. Dall'esterno sono aperte solo le porte pubbliche reali dei servizi.
NetBird mesh: perché non WireGuard classico
Ho usato WireGuard normale per molto tempo, ed era ok, finché i server non sono diventati più di tre. Dopo — gestire le configurazioni è diventato doloroso: ogni aggiunta di un peer si trasforma in "aggiorna la configurazione su tutti i nodi, non dimenticare allowed_ips, riavvia il demone, controlla di non aver rotto il routing verso un altro peer".
NetBird — è un piano di gestione sopra WireGuard. Tecnicamente sotto il cofano hai sempre lo stesso wg, ma:
- i nodi si trovano tra loro tramite un server di segnalazione, NAT-traversal automatico
- autenticazione tramite SSO (per me — tramite Authentik), senza condivisione di chiavi
- l'accesso è gestito nell'interfaccia web tramite policy, si possono creare gruppi di macchine e regole tra di loro
- proxy SSH integrato: si può accedere tramite
ssh user@machine.netbird.cloude ottenere autenticazione SSO (con approvazione in Authentik). Sulla nodo non è nemmeno necessario aprire la porta 22 verso l'esterno.
Ho installato l'agente NetBird:
- sull'host Proxmox
- su ogni VM che deve essere visibile dall'amministratore o da altre VM
- sul nodo di uscita
- sul mio laptop
Il risultato è che accedo da laptop a qualsiasi macchina con un solo comando, senza port forwarding, senza falle nel firewall, senza bastion-host aggiuntivi. E non c'è SSH aperto su nessuna delle VM private — sono tutte solo in vmbr1 più la mesh wt0.
VM vs container: come ho risolto per me stesso
Una regola semplice che funziona quasi sempre:
| Quando | Cosa scegliere |
|---|---|
| Carichi Docker | VM (Docker in LXC può essere capriccioso, specialmente con overlay2) |
| Database senza Docker | LXC (più leggero, più veloce, accesso diretto al disco tramite ZFS) |
| Servizi di sistema (PBS, monitoraggio, piccoli tool) | LXC |
| Applicazioni che necessitano di un proprio kernel / trucchi di rete | VM |
| Nodi VPN (WG, AmneziaWG) | LXC (minimo overhead) |
Alla fine, ho ottenuto circa questo:
VM (KVM):
forum— Discourse nel container launcher standard. Docker all'interno.services— un sacco di piccole cose: bot webhook, bot AI, pannello billing, Vaultwarden, Uptime Kuma. Tutto in Docker compose, per cartella per servizio.legacy— vecchio forum PHP (IPS) con PHP-FPM nativo e Caddy su VM. Non volevo impacchettarlo in Docker — troppo legacy, più facile lasciarlo com'era.dokploy— Docker Swarm + Dokploy per applicazioni utente (Next.js, Postgres, Redis), deploy tramite Git.
LXC:
pbs— Proxmox Backup Server (vedi sotto).pg16,pg17,pg18— tre Postgres separati di diverse versioni maggiori. Nota: diverse applicazioni richiedono versioni maggiori diverse, e non voglio doverle gestire tutte insieme.dashboards— un container con Homarr (pagina di avvio), Uptime Kuma e Beszel hub. Tutti e tre nello stesso compose, perché sono logicamente correlati — osservare.
Ogni VM riceve un IP statico in 10.10.10.x (per intervalli, così è più facile cercare), 2-8 core vCPU e 2-8 GB di RAM a seconda del carico. I core possono essere sovraprovisionati in sicurezza — in totale ho assegnato più core alle VM di quanti ne abbia fisicamente, e funziona perfettamente, finché nessuno non raggiunge contemporaneamente il limite della CPU.
Reverse proxy: Caddy a due piani
All'interno di Proxmox ho installato Caddy sull'host, non in una VM. Perché? Perché deve ascoltare le porte pubbliche del server dedicato e instradare in base all'Host-header alla VM corretta, e creare una VM separata con port forwarding per questo sarebbe uno strato aggiuntivo inutile. Caddy è leggero, scritto in Go, unit systemd, configurazione in un unico file.
Questo Caddy host ascolta solo la porta 8080 HTTP e fa proxy verso i backend su 10.10.10.x. Non ha TLS su di esso.
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}
E la terminazione TLS e i certificati — su un nodo di uscita separato, in Docker, con lo stesso principio:
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 — questo è l'indirizzo NetBird del server dedicato. La richiesta arriva al nodo di uscita via HTTPS, viene decifrata, passa nella mesh NetBird in HTTP verso il server dedicato, lì il Caddy di Proxmox fa il matching per Host e instrada alla VM interna. Tra il nodo di uscita e il server dedicato, HTTP è per design — ci fidiamo della mesh, non c'è traffico esterno qui.
Perché il nodo di uscita
Diverse ragioni:
- Aggirare i blocchi. Alcune sottoreti OVH in Russia sono bloccate. Il nodo di uscita si trova presso un provider il cui IP non è bloccato. Il DNS dei domini pubblici punta lì.
- Terminazione TLS in un unico posto. Solo esso emette i certificati, solo il suo IP è nelle challenge di Let's Encrypt. Questo semplifica tutti i controlli DNS e permette al server dedicato di essere completamente invisibile da Internet sulle porte 80/443.
- Strato aggiuntivo di isolazione. Se qualcuno inizia un DDoS, DDoS-a il nodo di uscita, non il server dedicato. Sul nodo di uscita ho dati minimi, ricrearlo — 10 minuti.
- Separazione del frontend pubblico dal backend. Posso tranquillamente eseguire aggiornamenti di Caddy / sperimentare sul server dedicato, mentre l'interfaccia reale verso Internet è separata e stabile.
Unico svantaggio: si aggiungono circa 5-15 ms a ogni richiesta a causa di un hop aggiuntivo. Per il web è impercettibile.
Inoltre, per alcuni servizi, c'è un Caddy diretto sull'host sulle porte 80/443 senza il nodo di uscita — per quei domini che non ho bisogno di inoltrare attraverso il punto di uscita (ad esempio, endpoint di stato, monitoraggio, servizi dove il DNS punta già al server dedicato). Questo funziona in parallelo: Caddy sull'host ascolta sia :8080 (interno dal nodo di uscita), sia :80/:443 (diretto da Internet), i percorsi sono diversi.
Migrazione: come ho traslocato senza lacrime e quasi senza downtime
Questa è stata la fase più spaventosa. Cinque server, circa trenta servizi, un forum in produzione con utenti attivi, un sistema di billing con abbonamenti attivi, database che in nessun caso potevano essere persi.
Strategia — migrazione di un servizio alla volta, in ordine "dal meno critico al più critico":
- Prima sono stati spostati piccoli tool e siti statici (se qualcosa va storto — nessuno se ne accorgerà).
- Poi bot e servizi in background (anch'essi sopportano un minuto di downtime).
- Poi database (con ripristino preventivamente verificato).
- Poi applicazioni che dipendono da questi DB.
- Alla fine — il forum principale con un lungo ciclo finale di sincronizzazione e cambio DNS.
Canale di migrazione
All'inizio ho semplicemente attivato NetBird sui vecchi server. Questo ha risolto subito due cose: posso accedere in sicurezza tramite SSH alle reti interne, e rsync passa su WireGuard tramite NetBird, senza esporre i dati su Internet aperto.
Il comando di battaglia per ogni servizio era più o meno questo:
1# Dal nuovo server dedicato, tramite IP NetBird del vecchio server:
2rsync -avzP --delete \
3 -e "ssh -p 5322" \
4 root@100.76.108.210:/var/discourse/shared/standalone/ \
5 /target/discourse/shared/standalone/
Per i database — dump + restore, non copia di file. Non copiate mai file Postgres/MySQL "a caldo", è un biglietto per l'aldilà.
1# Sul vecchio
2docker exec -it shm-vsem-mysql mysqldump -u root -p shm-vsem | \
3 gzip > /tmp/shm-vsem.sql.gz
4
5# Sul nuovo, tramite 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
Per Discourse — discourse backup / discourse restore nativi tramite ./launcher enter app. All'interno raccoglie correttamente il dump di Postgres + upload + configurazioni, e ripristina esattamente allo stesso modo.
Cambio DNS
Dopo aver avviato il servizio nella nuova posizione e verificato che funzionasse con l'indirizzo interno — ho avviato entrambe le installazioni in parallelo per 5-15 minuti, per osservare la sincronizzazione dei dati, e poi ho cambiato il DNS sul nuovo server. Avevo precedentemente ridotto il TTL delle voci a 60-300 secondi un giorno prima del cambio.
Per Discourse, dove gli utenti attivi postano continuamente, ho fatto così:
- Ho configurato tutto nella nuova posizione.
- Ho eseguito la validazione (login, posting, upload di file su S3, ricerca).
- Sull'installazione vecchia ho attivato
read_only_mode(Discourse lo supporta nativamente). - Ho fatto un rsync finale degli upload + un dump finale del DB.
- Ho avviato sul nuovo, disattivato read-only.
- Ho cambiato il DNS.
- Sulla vecchia non ho toccato nulla per un'altra settimana — in caso di "e se poi".
Downtime effettivo per gli utenti — circa due minuti per servizio.
Sorprese lungo la strada
- Permessi durante
pct restore. LXC sottounprivileged 1mappa gli UID con uno spostamento di 100000. Se sul vecchio server i tuoi file sono sotto UID 1000, sul nuovo saranno sotto UID 101000, e l'applicazione non li vedrà. Si risolve o conchowndopo il restore, o con--unprivileged 0se si comprendono i rischi. - Docker ipv6 nelle VM OVH a volte si attivava male — ho risolto disattivando ipv6 nel demone Docker (
"ipv6": false). - Ora sulle VM. Diverse volte ho riscontrato una discrepanza nell'ora di sistema tra host e VM, che rompeva TLS e firme. Rimedio —
systemd-timesyncdochronysu ogni VM, ehost.use-timenell'agente Proxmox. - Migrazione di Discourse tra due versioni. Se sul vecchio server Discourse è più recente di quello che hai installato sul nuovo — il
restorefallirà. La versione deve coincidere o essere superiore sulla nuova installazione. - Registry delle immagini per Dokploy. Quando ho spostato i metadati di Dokploy tramite il dump
dokploy-postgres, nel DB sono rimasti riferimenti al vecchio registry locale sull'IP vecchio. Le applicazioni, tentando di avviarsi, cercavano in100.76.117.115:5000e fallivano conNo such image. Soluzione — ricompilare ogni applicazione nell'interfaccia utente di Dokploy; la compilazione locale inserisce l'immagine già sul nuovo server.
Backup: PBS all'interno dello stesso server dedicato + offsite
Qui c'è stata un'apposita discussione filosofica con me stesso: fare il backup sullo stesso hardware o no. Risposta: sia sì che no.
Livello 1 — snapshot ZFS sul pool stesso. Sono quasi gratuiti (copy-on-write), vengono creati istantaneamente, ripristinati istantaneamente. Mantengo snapshot automatici di rpool/data ogni 15 minuti con retention di 24 ore, più snapshot giornalieri con retention di una settimana. Viene usato zfs-auto-snapshot. Protezione da "oh, ho appena cancellato il DB di produzione":
1apt install zfs-auto-snapshot
2# Poi fa da sé tramite cron creando frequent/hourly/daily/weekly/monthly
Livello 2 — Proxmox Backup Server in un LXC separato sullo stesso host. Questi sono backup incremental completi di VM/LXC tramite vzdump, deduplicati a livello di chunk. PBS memorizza gli snapshot in un dataset ZFS separato, li vede come repository, ogni VM ha la sua catena di incrementali.
Configurazione in Proxmox: Datacenter → Backup → Add. Programmazione: ogni giorno alle 4 del mattino, retention keep-daily=7 keep-weekly=4 keep-monthly=3. Tutto questo tramite mouse.
Posizionare PBS in LXC sullo stesso host è un compromesso. Pro: non serve hardware separato, il backup avviene tramite localhost, la velocità è come quella di un SSD locale, retention/dedup funziona perfettamente. Contro: se muore l'intero server dedicato, muoiono anche i backup. Per questo c'è...
Livello 3 — sincronizzazione del repository PBS in S3 offsite. PBS supporta il sync job in uno storage compatibile con S3. Ogni notte copio gli snapshot su un bucket separato nello S3 di Selectel (puoi usare qualsiasi — Backblaze, Wasabi, Cloudflare R2). La retention lì è di due settimane, perché non serve di più: per la conservazione a lungo termine c'è PBS locale, e offsite — è un'assicurazione contro il "tutto il data center è andato a fuoco".
Livello 4 — dati delle applicazioni separatamente. Discourse fa da sé i propri backup e li carica su S3 (questa è una sua funzionalità nativa). Quindi, anche se PBS e l'intero server dedicato dovessero sparire, avrò ancora backup consistenti di Discourse nel loro cloud.
1# Ripristino di un'intera VM da PBS — letteralmente un comando:
2pvesm list backup-pbs # vedere cosa c'è
3qmrestore backup-pbs:backup/vzdump-qemu-201-2026_04_06-04_00_03.vma.zst 999 \
4 --storage local-zfs
5# e dopo un minuto hai una copia della VM del forum sul momento delle 4 del mattino con VMID 999.
Questo è un punto critico: i backup che non hai provato a ripristinare — non sono backup. Una volta al mese faccio un restore di test di una VM casuale su un VMID vuoto, verifico che si avvii, che l'applicazione al suo interno funzioni, e la cancello. Noioso, ma una volta proprio così ho scoperto che uno dei cron scriveva in /tmp/..., che durante il backup veniva saltato, e l'applicazione dopo il restore richiedeva un passaggio manuale.
Monitoraggio
Non mi piace raccogliere conglomerati come Prometheus + Grafana + Alertmanager + Loki quando ho 15 macchine. Quindi mi sono fermato a tre strumenti leggeri in un unico LXC:
- Beszel — raccoglie metriche dagli agenti su ogni VM (CPU, RAM, dischi, interfacce di rete, container Docker). L'hub vive in LXC, gli agenti — su ogni VM/LXC tramite unit systemd. L'autenticazione di Beszel è propria, tramite PocketBase, che a volte è scomodo (vedi sotto per le insidie), ma nel complesso funziona.
- Uptime Kuma — controlli via HTTP/HTTPS/Ping/TCP. Ci ho collegato tutto il pubblico (domini), tutti i servizi interni (tramite NetBird), e i ping su tutti i nodi della mesh. Alert — in Telegram.
- Homarr — pagina di avvio con link a tutte le interfacce amministrative. Per non ricordare su quale porta si trova l'UI di PBS, su quale Dokploy, su quale Vaultwarden. Apro semplicemente Homarr e clicco.
Tutti e tre nello stesso Docker compose, nello stesso LXC, accessibili solo tramite NetBird.
Insidia con Beszel, affinché tu non ci inciampi: Beszel ha due tabelle utente in PocketBase —
_superusers(per CLI/API) eusers(per l'interfaccia web). Se resettate la password tramite CLIsuperuser upsert— resettate solo_superusers, e nell'UI accedete tramiteuserse la password non funziona. Si risolve con una richiesta PATCH a/api/collections/users/records/<id>tramite API REST.
Cosa ho ottenuto alla fine
Dopo circa tre settimane di migrazione, quando si sono stabilizzati gli ultimi servizi, ho tirato le somme:
| Parametro | Prima | Dopo |
|---|---|---|
| Server / fatture | 5 presso 3 provider | 1 server dedicato + 1 micro-VPS |
| Pagamenti al mese | ~$X | ~$X/2 |
| Risorse libere | "sembra sufficiente" | 8 vCPU e 30 GB di RAM in riserva |
| Backup | rsync-cron in S3 su ogni VPS | PBS + offsite + snapshot ZFS |
| Ripristino | "beh, un giorno o l'altro" | 2-5 minuti per VM da PBS |
| Accesso SSH | 5 porte e chiavi diverse | una mesh NetBird con SSO |
| Isolazione servizi | calderone comune | ognuno nella propria VM/LXC |
| Snapshot prima dell'aggiornamento | "prega" | qm snapshot 201 pre-update |
| Interfaccia web per compiti di routine | quale interfaccia web | UI Proxmox |
La cosa principale che mi ha dato emotivamente — ho smesso di aver paura di toccare qualcosa. Ogni azione pericolosa (aggiornamento, migrazione, esperimento) ora inizia con uno snapshot e finisce o con un commit, o con un rollback in 30 secondi. Ho iniziato a provare più spesso cose nuove, perché il costo dell'errore è diminuito di un ordine di grandezza.
Checklist, se volete ripetere
Se ti ritrovi con gli stessi sintomi miei — ecco una breve checklist, in quale ordine ha senso procedere:
- Calcola quanto paghi per tutte le VPS insieme, e confrontalo con il prezzo dei server dedicati di OVH/Hetzner/LeaseWeb. Ti sorprenderai.
- Prendi RAM ECC, se pianifichi ZFS. Non risparmiare.
- Prendi almeno due dischi in mirror. Un disco solo — non è un'opzione per la produzione.
- Installa Proxmox tramite KVM/IPMI, non tramite rescue + debootstrap. Meno dolore.
- Subito configura chiavi SSH, porta non standard, disabilita password, installa fail2ban.
- Subito limita ZFS ARC. Di default consumerà metà della RAM.
- Crea un secondo bridge
vmbr1per la rete privata con NAT. Tutte le VM/LXC lì. Nessun IP pubblico sulle VM. - Attiva NetBird (o Tailscale, o Headscale) prima di iniziare a migrare. Questo è il tuo canale di migrazione e accesso amministrativo.
- Non aprire l'UI di Proxmox all'esterno. Solo tramite la mesh VPN.
- Una piccola VPS di uscita presso un altro provider per la terminazione TLS e l'aggiramento dei blocchi — non è obbligatorio, ma molto comodo. Il prezzo è irrisorio, i vantaggi molti.
- PBS in un LXC separato + sync offsite in S3. I backup devono essere automatici.
- Almeno una volta al mese fai un restore di test. Altrimenti non hai backup, ma file per auto-rassicurarti.
- Migra un servizio alla volta, in ordine di criticità crescente. Non cercare di "traslocare tutto nel weekend".
- Riduci il TTL del DNS un giorno prima del cambio, non un'ora prima.
- Tieni la documentazione per ogni servizio: dove sono le configurazioni, come avviarlo, come fare il backup, come ripristinarlo. Tra sei mesi ti ringrazierai da solo.
Cosa resta da fare
Per completezza: la migrazione non è ancora finita al 100%. Restano:
- Un paio di pannelli Remnawave dal vecchio server (pianificati per il prossimo weekend).
- Spostamento di Caddy con plugin (c'è una build custom), al momento funziona temporaneamente.
- Vecchio CrowdSec — lo installo da zero separatamente, non lo trasferisco.
Ma tutto ciò che è critico (forum, billing, bot, database) — è già sul server dedicato, funziona stabilmente, viene backuppato e monitorato.
Se hai domande sui passaggi specifici, configurazioni o insidie su cui sono inciampato più nel dettaglio — scrivi nei commenti, cercherò di approfondire. Particolarmente interessante sentire come risolvete compiti simili su altri hypervisor (XCP-ng, ESXi, o anche su Docker Swarm nudo) — forse mi sfugge qualcosa.
In bocca al lupo con il consolidamento delle tue infrastrutture. Meno fatture, più controllo — ne vale la pena.