Una server blade nera opaca e illuminata in un data center buio, che riversa un nastro di particelle di dati color smeraldo in un duplicato wireframe luminoso di se stessa, sigillato da un lucchetto luminoso
Guida al backup

Fare il backup di un VPS che puoi davvero ripristinare

Quasi chiunque gestisca un server ha qualcosa che chiama backup. Molti meno lo hanno mai ripristinato, e la distanza tra queste due affermazioni è esattamente dove i dati spariscono davvero. Ecco come costruire una copia che sopravvive alle tre cose che distruggono davvero i server — le tue stesse mani, una compromissione, e un account che smetti di controllare — senza incollare silenziosamente un'identità verificata su una macchina anonima.

Di ogni byte che fa girare questa rete teniamo tre copie, e le abbiamo ripristinate, di proposito, in un martedì qualsiasi, più volte di quante ce ne sia mai servita una in un'emergenza. Non è diligenza fine a se stessa. È l'unico modo per sapere che ciò che sta nel repository è un backup, e non una cartella di rumore cifrato con un nome rassicurante.

Il consiglio che segue è deliberatamente fuori moda. Non prevede un prodotto di backup con una dashboard, un abbonamento mensile o una coda di supporto, perché ognuna di queste cose è un account, e un account è un nome, una carta e una giurisdizione — tre cose per tenere le quali lontane da questo server potresti aver speso un impegno reale. Prevede una seconda macchina, una quarantina di righe di shell, e un'abitudine che quasi nessuno mantiene.

Dovresti riuscire a completare tutto questo in una serata. Gli strumenti hanno il sapore di Debian 13 e Ubuntu 24.04; tutto si trasferisce alle derivate RHEL con le sostituzioni ovvie. Se il tuo server è nuovo, fai la blindatura della prima ora prima di questa pagina e non dopo — fare il backup di una macchina che è già di qualcun altro è un modo per preservare il suo lavoro.

Cosa distrugge davvero i dati su un server a noleggio

Chiedi a chiunque perché fa il backup e la risposta è di solito “nel caso il disco muoia”. Su un VPS moderno, questo è quanto di più vicino esista alla cosa meno probabile che ti capiterà. Il tuo volume risiede su NVMe in un array ridondato con un hypervisor che ti migrerà via da un host in avaria prima ancora che tu te ne accorga; il guasto del disco è un problema del provider, ed è un problema già risolto. Costruire l'intera strategia di backup attorno a questo è come comprare un estintore per una casa che si allaga.

Ecco cosa porta via davvero i dati alle persone, all'incirca in ordine di frequenza. Primo, e con ampio margine: tu. Un rm -rf con una variabile che si è espansa nel nulla. Una DROP DATABASE sul terminale sbagliato perché due schede sembravano identiche. Uno script di migrazione lanciato due volte. Un docker compose down -v in cui la -v era pura memoria muscolare. Non sono casi esotici; sono un martedì qualunque.

Secondo, le applicazioni che cancellano i propri dati. Un aggiornamento che esegue una migrazione distruttiva e fallisce a metà strada. Un plugin che svuota una directory di cache che si scopre non essere affatto una cache. Una rotazione dei log configurata sul percorso sbagliato. Terzo, la compromissione — e nota che questo è ormai il caso in cui un attaccante vuole che i backup spariscano, il che cambia il progetto in modi su cui torniamo più avanti. Quarto, e facile da dimenticare su un host no-KYC: perdere l'account. Un saldo che è arrivato a zero mentre eri via, una voce del password manager che non hai mai annotato, un indirizzo email che hai abbandonato. Noi sospendiamo entro poche ore da un saldo a zero e distruggiamo sette giorni dopo, e questo è deliberatamente severo — è l'aritmetica che ci permette di non chiederti chi sei. Non esiste un operatore di supporto che possa cercare la tua identità e fare un'eccezione, perché non esiste un'identità da cercare.

Quest'ultima categoria è quella che separa un hosting privacy-first da quello mainstream, e dovrebbe plasmare il tuo piano. Ogni controllo che rende questo server difficile da attribuirti rende anche difficile, per chiunque — noi compresi — restituirtelo. Il backup è ciò che trasforma questo scambio da un rischio in una scelta.

Perché uno snapshot non è un backup

Gli snapshot sono meravigliosi e dovresti usarli. Fanne uno prima di ogni aggiornamento del kernel, ogni migrazione di database, ogni volta che stai per fare qualcosa che preferiresti poter annullare. Si ripristinano in pochi secondi, costano quasi nulla, e ti salveranno un pomeriggio una dozzina di volte l'anno.

Non sono comunque backup, per tre ragioni strutturali che nessuna quantità di marketing del provider può cambiare. Vivono dentro lo stesso account: chiunque possa accedere al tuo pannello può cancellarli, e se l'account smette di funzionare gli snapshot smettono di esistere insieme a lui. Vivono nello stesso dominio di guasto: stesso provider, stesso control plane, spesso lo stesso cluster di storage, il che significa che un singolo evento negativo può plausibilmente portarsi via sia l'originale che la copia. E sono opachi: uno snapshot di una macchina in esecuzione cattura un database a metà scrittura con la stessa fedeltà con cui cattura tutto il resto, quindi il ripristino di uno snapshot di un server MySQL molto attivo ti dà uno scenario di crash-recovery invece di un database pulito.

La vecchia regola del 3-2-1 — tre copie, su due tipi di supporto, una delle quali off-site — viene di solito recitata senza notare che la clausola centrale è priva di senso su un'infrastruttura a noleggio. Non hai due tipi di supporto. Hai un dispositivo a blocchi virtuale, e un altro dispositivo a blocchi virtuale, ed entrambi sono la SAN di qualcun altro. Quello che sopravvive alla traduzione su un VPS è la parte che è sempre stata importante: almeno una copia deve stare in un posto che un singolo evento negativo non può raggiungere. Provider diverso o quantomeno regione diversa, credenziali diverse, account diverso, e — se il motivo per cui usi hosting offshore è legale piuttosto che tecnico — una giurisdizione diversa, in modo che un singolo ordine del tribunale non colpisca entrambe le copie in una volta.

La destinazione fa parte del tuo modello di minaccia

Questa è la sezione che gli altri tutorial sul backup non hanno, ed è quella che conta di più se hai pagato questo server in Monero.

Il consiglio di default che si trova ovunque su internet è di spingere (push) i tuoi backup verso uno storage a oggetti: Backblaze B2, Amazon S3, Wasabi, un Google Drive via rclone. È economico, è durevole, funziona. Ma, con un solo comando, vanifica anche gran parte di ciò che stavi facendo. Aprire quell'account ha richiesto una carta e di solito un documento d'identità. Dal momento in cui viene caricato il primo snapshot, quel provider detiene un registro con marca temporale che associa il tuo nome e i tuoi dati di pagamento all'indirizzo IP del tuo server, aggiornato ogni ora, per sempre. Non hai verificato la tua identità con noi; l'hai verificata con loro, e poi hai tracciato una linea tra i due.

La seconda metà è peggiore, ed è la parte che sfugge a molti. La chiave API che autorizza quei caricamenti è un file sul server di produzione. Chiunque ottenga root su quella macchina — o chiunque acquisisca legalmente il disco — non ottiene soltanto i tuoi dati. Ottiene una credenziale che risolve, con una sola chiamata API, in un'identità di fatturazione. Il server anonimo è diventato un cartello indicatore che punta alla tua banca.

Niente di tutto questo rende sbagliato lo storage a oggetti. Lo rende una decisione anziché un default. Tre vie d'uscita, in ordine decrescente di quanto bene preservano ciò da cui sei partito:

  • Un secondo VPS no-KYC, idealmente in una giurisdizione diversa dalla produzione, pagato dallo stesso saldo in cripto. La destinazione eredita le proprietà di anonimato della fonte invece di contraddirle. È quello che facciamo noi, ed è ciò che il resto di questa guida presuppone.
  • Un provider di storage che accetta cripto senza identità. Esistono; sono più piccoli; verifica che il percorso di pagamento sia davvero privo di identità prima di fidarti del marketing, e controlla i costi di egress prima di scoprirli durante un ripristino.
  • Una macchina che possiedi fisicamente, con un pull via WireGuard da dietro la tua stessa connessione. Eccellente per l'anonimato, scarsa per la disponibilità, e significa che la tua velocità di ripristino è la velocità di upload di casa tua. Ragionevole come terza copia, debole come unica copia.

Qualunque cosa tu scelga, l'account che detiene i backup non dovrebbe condividere credenziali, indirizzo email o percorso di recupero con l'account che detiene la produzione. Il punto è proprio che un login compromesso non deve raggiungerli entrambi. È la stessa disciplina del tenere le identità separate altrove, applicata all'angolo meno affascinante del sistema.

Push, pull, e il motivo per cui il ransomware trova i tuoi backup

Quasi ogni tutorial sul backup produce la stessa architettura: un cron job sul server di produzione che si autentica su un repository remoto e ci scrive sopra. È semplice, funziona, e ha una proprietà che nessuno menziona — il server di produzione detiene una credenziale che può cancellare l'intero repository. Fare pruning dei vecchi snapshot richiede diritti di cancellazione, quindi la chiave che esegue il tuo job notturno è anche la chiave che svuota la cassaforte.

Pensa a cosa significa questo durante una compromissione. Un attaccante con root sulla macchina non ha bisogno di cercare i tuoi backup; gli hai gentilmente lasciato una credenziale funzionante e un file di configurazione che nomina il repository. Enumerare e distruggere i backup prima di far scattare qualcosa di visibile non è un raffinamento ipotetico — è prassi standard, perché è ciò che trasforma un incidente in una trattativa. Il tuo job notturno era la ricognizione.

Esistono tre schemi, e la differenza tra loro riguarda interamente quale macchina detiene quale chiave.

Push semplice è quello descritto sopra. La produzione ha lettura-scrittura-cancellazione. Comodo, e fallisce completamente esattamente nello scenario in cui hai più bisogno di un backup. Usalo solo se l'unica minaccia che ti interessa davvero sono le tue stesse dita.

Push append-only mantiene la stessa forma ma rimuove il verbo pericoloso. Il repository è servito da qualcosa che comprende il protocollo e rifiuta le cancellazioni: rest-server --append-only per restic, oppure borg serve --append-only forzato da authorized_keys. La produzione può creare nuovi snapshot e non può rimuovere quelli vecchi. Il pruning avviene più tardi, da un altro posto, usando una chiave diversa. È un grande miglioramento per circa venti minuti di lavoro, e per la maggior parte delle persone è il punto di arresto giusto.

Pull inverte la connessione. L'host di backup si allunga verso la produzione via SSH, copia ciò di cui ha bisogno, ed esegue lo strumento di backup localmente sul proprio disco. La produzione non detiene alcuna credenziale di backup — non c'è nulla da trovare su quella macchina, perché la macchina non sa nemmeno dove vivano i suoi backup. Questo è l'assetto più solido, ed è quello che la procedura passo-passo qui sotto costruisce.

Sii onesto su quanto costa il pull, perché non è gratis. Hai spostato la chiave invece di eliminarla: l'host di backup ora detiene una chiave SSH verso la produzione, quindi una compromissione dell'host di backup si propaga in avanti fino al sistema live. È uno scambio migliore — l'host di backup non esegue nulla, non espone altro che SSH, ed è un bersaglio molto più piccolo di un server web pubblico — ma resta uno scambio, e chiudi la maggior parte del divario residuo forzando quella chiave su un comando di sola lettura, così che non possa essere usata per nient'altro che leggere file.

Scegliere lo strumento: restic, Borg o il semplice rsync

Tre strumenti coprono essenzialmente ogni caso, e la scelta è molto meno tormentata di quanto suggeriscano i thread dei forum.

restic cifra di default, deduplica tra gli snapshot, parla SFTP, S3, REST e una dozzina di altri backend, e viene distribuito come un singolo binario statico che puoi far girare su qualsiasi cosa. Il suo formato di repository è content-addressed, quindi uno snapshot è economico e i dati identici vengono memorizzati una sola volta, indipendentemente da quante macchine li inviano. I costi sono reali ma modesti: vuole memoria proporzionale all'indice del repository, e un'esecuzione interrotta può lasciare un lock stantio che l'esecuzione successiva rifiuta di scavalcare finché non lo rimuovi con restic unlock. Questa è la raccomandazione di default, ed è ciò che usano i passaggi qui sotto.

Borg deduplica meglio, comprime meglio, ed è nettamente più veloce su repository con milioni di file piccoli. Il suo compromesso è l'accoppiamento: Borg deve essere installato su entrambi i lati e le versioni devono corrispondere da vicino, un repository è pensato davvero per un solo client, e non ha un backend nativo per lo storage a oggetti senza un livello di supporto aggiuntivo. Se la tua fonte è uno spool di posta corposo o un filesystem pieno di file piccoli, e controlli entrambe le macchine, Borg userà la metà dello spazio. La sua modalità --append-only è anche l'implementazione più pulita dell'idea tra i due strumenti.

rsync non è uno strumento di backup, e far finta del contrario è come si finisce con una copia fedelmente rispecchiata di una directory corrotta. Non ha versioning, non ha deduplicazione e non ha cifratura a riposo; rsync --delete propaga il tuo errore alla copia alla velocità del cavo. È comunque lo strumento giusto per spostare byte tra due macchine che controlli, che è esattamente il lavoro che svolge in un progetto a pull, con restic che fornisce il versioning e la cifratura una volta che i byte sono atterrati. Usa ciascuno per quello che è.

Una cosa da non fare: non inventarti una soluzione fatta in casa con tar e un nome file con data. Funziona per circa quattro mesi, fino al giorno in cui il disco si riempie perché niente è mai scaduto, o al giorno in cui scopri che una copia completa ogni notte di un dataset da 40 GB corrisponde a 1,2 TB al mese di storage che stai pagando per conservare trenta cose quasi identiche.

Cosa includere — e i database che ti tradiranno

L'istinto è fare il backup dell'intero filesystem. Resistigli. Un filesystem root è per lo più pacchetti di distribuzione che puoi reinstallare in novanta secondi, e includerli costa storage, tempo di trasferimento e, peggio, attenzione — un backup da 40 GB che nessuno vuole testare è meno utile di uno da 900 MB che viene ripristinato ogni trimestre.

Ciò che davvero non può essere ricostruito è poca cosa: /etc (l'intera configurazione, e il motivo per cui una ricostruzione richiede un'ora invece di un weekend), /home e /root, /srv e /var/www, lo stato applicativo sotto /var/lib/ e /opt/, i volumi Docker con nome, le unit di cron e systemd che hai scritto tu, e i dump del tuo database. Salta /proc, /sys, /dev, /run, /tmp, /var/cache, i file di swap, i socket e /var/lib/docker/overlay2 — quest'ultima è ricostruibile dal tuo file compose ed è spesso la singola directory più grande sul disco.

Ora la parte che silenziosamente rovina i ripristini. Copiare la directory dei dati di un database mentre il database è in esecuzione produce un insieme di file che molto probabilmente sarà inutilizzabile. Il motore tiene lo stato in memoria, scrive su più file in un ordine che conta, e la tua copia percorre quell'albero nell'arco di svariati minuti catturando file diversi in istanti diversi. InnoDB a volte se ne riprenderà con un crash-recovery e a volte no; il guasto emerge mesi dopo, durante un ripristino in cui stai già avendo una brutta giornata.

Fai invece il dump attraverso il motore, poi metti il dump in backup:

# MariaDB / MySQL — InnoDB gets a consistent snapshot from one transaction
mariadb-dump --single-transaction --quick --routines --triggers --events \
  --all-databases | zstd -T0 > /var/backups/db/all-$(date -u +%F).sql.zst

# PostgreSQL — pg_dump is consistent by design; grab roles separately
pg_dumpall --globals-only > /var/backups/db/globals.sql
pg_dump -Fc -Z6 mydb    > /var/backups/db/mydb.dump

# SQLite — never cp a live database; WAL mode makes that a torn file
sqlite3 /var/lib/app/app.db ".backup /var/backups/db/app.db"

Due note a margine che vale la pena avere. --single-transaction ti dà coerenza solo per InnoDB — se una tabella è ancora MyISAM viene copiata fuori dalla transazione e può risultare incoerente rispetto al resto, quindi converti quelle tabelle o mettile sotto lock. E una volta che stai facendo il dump, escludi la directory dei dati live dal passaggio sui file. Fare il backup di entrambi significa conservare una grande copia lacerata accanto a quella buona, e dare a un futuro ripristino due candidati in cui uno è silenziosamente sbagliato.

I container non cambiano il principio, solo il percorso: esegui il dump con docker compose exec -T db mariadb-dump … e metti in backup il file risultante, non il volume sottostante.

Dove vive la chiave di cifratura

La cifratura lato client è l'unica ragione per cui è accettabile conservare i tuoi dati su una macchina diversa da quella da cui provengono. Sia restic che Borg cifrano prima che qualsiasi cosa lasci la fonte, quindi l'host di backup detiene solo testo cifrato e può essere trattato come storage non fidato. Questa proprietà vale esattamente quanto la cura con cui gestisci la chiave, e non un briciolo di più.

La modalità di guasto è deprimentemente comune. La password del repository sta in /etc/restic/env sul server di produzione, il che va bene ed è necessario per l'automazione. Poi è proprio la produzione che perdi — distrutta, sequestrata, o semplicemente sparita insieme all'account — e ti ritrovi davanti a diverse centinaia di gigabyte di blocchi cifrati e alla progressiva consapevolezza che l'unica copia della chiave stava dentro proprio la cosa da cui ti stavi proteggendo dal perdere.

Quindi: la password sul server è una copia di lavoro, mai il verbale ufficiale. Il verbale ufficiale vive da qualche parte che sopravvive al server — un password manager il cui vault è a sua volta salvato altrove, oppure scritto su carta in un cassetto, o entrambi. Annota la posizione del repository e il comando di ripristino esatto accanto ad essa, perché una passphrase senza contesto è un rompicapo che risolverai sotto stress tra diciotto mesi. E dedica cinque minuti a verificarla: da una macchina diversa, con solo ciò che è nel password manager, esegui restic snapshots contro il repository. Se funziona, hai un backup. Se richiede qualcosa che esiste solo sulla produzione, hai una cartella molto costosa.

Entrambi gli strumenti supportano più chiavi su un solo repository (restic key add), che è il modo pulito per dare accesso al job di pruning o a un secondo amministratore senza condividere la passphrase originale. E se il disco di produzione è cifrato con LUKS, tieni i due segreti genuinamente separati — conservare la passphrase di restic dentro il volume LUKS e la passphrase di LUKS dentro il repository restic è un ciclo che si blocca in modo sicuro su entrambi i lati.

Retention: la trappola di conservare solo una settimana

La retention sembra una questione di costo dello storage, ma è in realtà una questione di latenza nel rilevamento. Il numero che conta non è quanto disco vuoi spendere; è per quanto tempo un problema può passare inosservato nel tuo sistema. Qualunque sia quel periodo, il tuo backup più vecchio deve essere più vecchio di esso.

Sette snapshot giornalieri sembra generoso ed è scarso nella pratica. Una tabella corrotta che nessuno interroga, una cancellazione lenta causata da un cron job difettoso, un'intrusione rimasta silenziosa per un mese prima di fare qualcosa di visibile — tutte queste cose impiegano di norma più di una settimana a emergere, e se la tua storia è profonda sette giorni allora ogni snapshot che conservi è già contaminato. Il dwell time nelle intrusioni reali si misura regolarmente in settimane. Gli snapshot mensili economici sono la difesa specifica contro questo, e la deduplicazione li rende davvero molto economici: un mensile conservato per un anno aggiunge una frazione di una copia completa, perché solo i blocchi effettivamente cambiati vengono memorizzati due volte.

restic forget \
  --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 2 \
  --prune

Questa scala — una settimana di giorni, un mese di settimane, un anno di mesi, un paio d'anni di anni — corrisponde a circa ventisei snapshot e, su dati tipici, a ben meno del doppio della dimensione di una singola copia completa. È il default per cui faremmo battaglia, a meno che tu non abbia una ragione specifica per discostartene.

Due note operative. forget senza --prune rimuove solo le etichette, quindi lo spazio non viene recuperato finché non fai il prune — cosa che sorprende chi osserva un disco che non si riduce mai. E --prune richiede diritti di cancellazione sul repository, quindi in un progetto append-only o pull non gira sul server di produzione. Gira sull'host di backup, o dal tuo laptop, con una chiave che la produzione non ha mai visto. Questa separazione è tutto il punto della faccenda; non disfarla per comodità all'ultimo passaggio.

Automatizzarlo senza il fallimento silenzioso

Il classico disastro del backup non è un job che va in crash. È un job che smette di girare e non lo dice a nessuno, scoperto undici mesi dopo da qualcuno che ne aveva bisogno. Ogni elemento qui sotto esiste per rendere impossibile esattamente quell'esito.

Usa un timer systemd invece di cron. Ottieni log veri nel journal con gli exit status allegati, Persistent=true in modo che un'esecuzione mancata mentre la macchina era spenta avvenga al boot successivo invece di essere saltata per sempre, e RandomizedDelaySec in modo che una flotta non travolga l'host di backup esattamente alle 03:00. La notifica di fallimento di cron è un'email verso una mailbox locale che, su un server moderno, non va esattamente da nessuna parte.

# /etc/systemd/system/backup.timer
[Unit]
Description=Nightly encrypted backup

[Timer]
OnCalendar=*-*-* 03:17:00
RandomizedDelaySec=1800
Persistent=true

[Install]
WantedBy=timers.target

Poi aggiungi un dead-man's switch, che è la singola riga a più alto valore in tutta questa guida. Alla fine esatta dello script — dopo che il backup è andato a buon fine, non prima — fai una richiesta HTTPS verso un monitor che si aspetta di sentirti ogni giorno e avvisa quando non succede. Questo inverte la logica della notifica: invece di affidarti a un fallimento per generare un messaggio, è l'assenza stessa a diventare l'allarme. Un job morto da tre giorni diventa allora un messaggio nella tua casella di posta invece che una scoperta in piena crisi. Ospita tu stesso il monitor sull'host di backup se preferisci non creare un altro account di terze parti; sono poche righe e un timer a sé.

Infine, i piccoli rischi operativi, tutti quelli in cui siamo incappati almeno una volta. Un'esecuzione interrotta lascia un lock e ogni esecuzione successiva fallisce con un messaggio che smetti di leggere dopo la quarta notte — gestisci restic unlock in modo deliberato, non riflesso. Un disco di destinazione pieno fa fallire ogni job finché qualcuno non se ne accorge; genera un avviso sullo spazio libero, non solo sullo stato del job. Una chiave SSH con una scadenza, una host key ruotata, una regola nftables aggiunta durante un lavoro non correlato — ognuna di queste interromperà silenziosamente il pull. E systemctl enable --now, non solo start: un timer che non è mai stato abilitato funziona magnificamente fino al primo riavvio e poi non gira mai più.

L'esercitazione di ripristino

Tutto quanto sopra è preparazione. Questa è la parte che lo trasforma in un backup, ed è la parte che quasi tutti saltano.

Una volta al trimestre, fai il deploy di un VPS nuovo — lo Starter da $5 è più che sufficiente e la fatturazione è oraria, proporzionata al secondo, quindi l'intero esercizio costa un paio di centesimi. Ripristina al suo interno usando solo ciò che avresti in un disastro reale: l'indirizzo del repository, la passphrase dal tuo password manager, e la procedura scritta. Non usare deliberatamente nulla della produzione, perché nello scenario che stai provando la produzione non esiste. Porta su l'applicazione, punta una voce del file hosts verso il nuovo IP, clicca in giro. Poi distruggilo.

# on a throwaway machine, with nothing but the passphrase
export RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/prod
restic snapshots                      # can you even see the history?
restic restore latest --target /restore
restic check --read-data-subset=10%   # verify stored blocks, not just metadata

Quello che questo scova non è mai ciò che ti aspetti. È il file di configurazione che stava in una directory che la lista di inclusione non copriva. Il dump del database che è a zero byte da cinque settimane perché una password è cambiata e lo script non ha controllato il suo exit status. L'applicazione che non parte perché un secret vive in una variabile d'ambiente detenuta dall'orchestratore e non è mai stata nel filesystem. Il record DNS non documentato. Il certificato che va riemesso prima che qualcosa risponda sulla 443. Ognuno di questi è una correzione da dieci minuti in un pomeriggio tranquillo, oppure due ore brutte alle tre del mattino.

Annota quanto è durata l'esercitazione, dall'inizio alla fine. Quel numero — non la frequenza dei backup — è il tuo vero tempo di ripristino, ed è l'unica risposta onesta quando qualcuno chiede per quanto tempo saresti fuori uso. Aggiungi anche restic check --read-data-subset=5% a un timer mensile: legge e verifica un campione rotante dei blocchi effettivamente memorizzati anziché il solo indice, ed è così che trovi la corruzione silenziosa mentre esiste ancora una buona copia a cui tornare. Se mai sposti l'intero server verso un nuovo host, un ripristino provato è anche gran parte della migrazione già fatta.

  1. Fai l'inventario di ciò che davvero non si può ricostruire

    Prima di qualsiasi strumento, scrivi la lista. Percorri la macchina e chiediti, per ogni directory: se sparisse, potrei ricrearla da un package manager, un repository git o un file compose? Se sì, non appartiene al backup. Quello che resta è di solito molto più piccolo di quanto ci si aspetti — configurazione, dati utente, stato applicativo, dump del database.

    # the fast way to find what is actually big and stateful
    du -x -h -d2 / 2>/dev/null | sort -rh | head -30
    docker volume ls    # named volumes are state; overlay2 is not

    Trasforma il risultato in due file espliciti, /etc/restic/include.txt e /etc/restic/exclude.txt. Le liste esplicite battono le espressioni find ingegnose perché sono verificabili, e perché una nuova directory che compare sul server dovrebbe essere una decisione deliberata e non un'inclusione silenziosa.

  2. Fai il deploy del target di backup in una giurisdizione diversa

    Ordina un secondo VPS in una regione diversa da quella in cui gira la produzione — se la produzione è a Parigi, metti la copia a Reykjavík o Bucarest. Il punto è che nessun singolo evento legale o fisico raggiunga entrambe le macchine. Uno Starter da $5/mese porta 80 GB di NVMe, che dopo la deduplicazione conservano una storia molto lunga di un tipico piccolo server; il ciclo di fatturazione a 12 mesi dimezza quella cifra. Pagalo dallo stesso saldo in cripto, così la destinazione eredita l'anonimato della fonte invece di contraddirlo.

    Dagli un account separato dalla produzione se vuoi un isolamento completo delle credenziali. Poi blindalo esattamente come faresti con qualunque altra cosa — SSH solo a chiavi, firewall default-deny — e non installarci nient'altro. Il valore di questa macchina sta nel fatto che è noiosa: nessun server web, nessuna porta aperta oltre SSH, niente da sfruttare da internet.

  3. Crea una porta di sola lettura dall'host di backup verso la produzione

    Questo è il passaggio che lo rende un pull. Sull'host di backup, genera una chiave dedicata. Poi installa la sua metà pubblica sulla produzione con un comando forzato, così che la chiave possa fare esattamente una cosa: leggere file. Non può aprire una shell, inoltrare una porta, o scrivere nulla.

    # on the backup host
    ssh-keygen -t ed25519 -a 100 -f ~/.ssh/pull_prod -C "pull@backup"
    
    # on production, in /root/.ssh/authorized_keys — one line
    command="/usr/bin/rrsync -ro /",restrict ssh-ed25519 AAAAC3Nz… pull@backup

    rrsync viene distribuito insieme a rsync (/usr/bin/rrsync su Debian 13; /usr/share/doc/rsync/scripts/rrsync sulle release più vecchie) e -ro lo fa rifiutare tutto tranne le letture. restrict disabilita in una sola parola l'inoltro di porte, l'inoltro dell'agent, l'allocazione di PTY e X11. Verifica che la gabbia tenga prima di farci affidamento — ssh -i ~/.ssh/pull_prod root@production deve fallire nel darti una shell.

  4. Fai il dump dei database sulla produzione, con un proprio schedule

    La produzione conserva ancora un compito: produrre dump coerenti in una directory di staging che il pull raccoglierà. Questo non richiede alcuna credenziale di backup, ed è esattamente per questo che il progetto funziona.

    # /usr/local/sbin/dump-db.sh   (chmod 700)
    set -euo pipefail
    D=/var/backups/db; install -d -m 700 "$D"
    mariadb-dump --single-transaction --quick --routines --triggers --events \
      --all-databases | zstd -T0 > "$D/all.sql.zst.tmp"
    mv "$D/all.sql.zst.tmp" "$D/all.sql.zst"

    Nota lo schema scrivi-su-temporaneo-poi-rinomina: significa che il pull non può mai raccogliere un dump scritto a metà, qualunque sia il tempismo. set -euo pipefail non è un ornamento — senza di esso, un mariadb-dump fallito invia uno stream vuoto dentro zstd, che riesce comunque, e ottieni un file compresso valido che non contiene assolutamente nulla. Questo è il modo singolarmente più comune in cui un backup diventa silenziosamente inutile. Eseguilo da un suo proprio timer, mezz'ora prima del pull.

  5. Fai il pull dei dati verso l'host di backup

    Sull'host di backup, fai un rsync della lista di inclusione della produzione dentro un albero di staging. Solo i blocchi cambiati attraversano il filo, quindi dopo la prima esecuzione questo è veloce ed economico.

    # /usr/local/sbin/pull.sh   (chmod 700, runs on the BACKUP host)
    set -euo pipefail
    [email protected]
    # -r is spelled out on purpose: with --files-from, -a does NOT imply
    # recursion, and without it you silently copy empty directories.
    rsync -aHAX -r --delete --numeric-ids \
      -e "ssh -i /root/.ssh/pull_prod -o BatchMode=yes" \
      --files-from=/etc/backup/include.txt \
      --exclude-from=/etc/backup/exclude.txt \
      "$SRC:/" /srv/staging/prod/

    -aHAX preserva hardlink, ACL e attributi estesi, e --numeric-ids mantiene significativa la proprietà tra macchine i cui /etc/passwd differiscono. --delete qui è sicuro proprio perché lo staging non è il backup — la storia versionata vive nel repository restic costruito nel passaggio successivo, quindi una cancellazione che si propaga nello staging resta comunque recuperabile dallo snapshot di ieri.

  6. Inizializza il repository e conserva la chiave offline

    Sempre sull'host di backup, crea un repository restic locale e fai il backup dell'albero di staging al suo interno. Locale significa nessuna rete nel percorso critico, nessuna credenziale remota da rubare, e un ripristino che gira alla velocità del disco.

    apt install -y restic
    install -d -m 700 /etc/backup
    openssl rand -base64 32 > /etc/backup/pass   # write this into your password manager NOW
    chmod 600 /etc/backup/pass
    
    export RESTIC_REPOSITORY=/srv/restic/prod
    export RESTIC_PASSWORD_FILE=/etc/backup/pass
    restic init

    Copia quella passphrase in un password manager, e accanto ad essa scrivi il percorso del repository e il comando di ripristino. Poi dimostralo: da una terza macchina, usando solo il password manager, esegui restic -r sftp:backup@…:/srv/restic/prod snapshots. Se elenca gli snapshot, la chiave è genuinamente recuperabile. Se richiede qualcosa che esiste solo sull'uno o sull'altro server, sistemalo adesso invece di scoprirlo più avanti.

  7. Pianificalo, e trasforma il silenzio in un allarme

    Racchiudi il pull, l'esecuzione di restic e il prune in un unico script e guidalo con un timer systemd. Nota che forget --prune è sicuro da eseguire qui perché l'host di backup possiede legittimamente il repository — la produzione non ha mai detenuto una credenziale di cancellazione in nessun momento.

    # tail of /usr/local/sbin/backup.sh
    restic backup /srv/staging/prod --tag nightly
    restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 \
                   --keep-yearly 2 --prune
    curl -fsS -m 10 --retry 3 https://hc.example.net/ping/<uuid>  # only on success
    systemctl daemon-reload
    systemctl enable --now backup.timer
    systemctl list-timers backup.timer     # confirm the next run is where you expect

    Il curl gira solo se ogni comando precedente è riuscito, per via di set -e. Il monitor dall'altra parte si aspetta un ping giornaliero e avvisa in caso di assenza, il che trasforma un job che si è fermato silenziosamente in un'email invece che in un ritrovamento archeologico. enable --now, non solo start — un timer che non è mai stato abilitato sopravvive esattamente fino al primo riavvio.

  8. Esegui l'esercitazione di ripristino, e annota quanto ci hai messo

    Metti un promemoria ricorrente in qualunque cosa tu legga davvero, una volta al trimestre. Fai il deploy di uno Starter usa e getta, ripristina al suo interno con nient'altro che la passphrase e la procedura scritta, avvia l'applicazione, verifica che serva dati reali, distruggi la macchina. La fatturazione oraria significa che l'intera esercitazione costa un paio di centesimi.

    restic restore latest --target /restore
    zstd -dc /restore/var/backups/db/all.sql.zst | mariadb
    systemctl start nginx app
    curl -H 'Host: example.com' http://127.0.0.1/health

    Registra il tempo trascorso e ogni sorpresa in cui sei incappato, poi correggi le sorprese nello script di backup invece che nella tua memoria. Quel tempo trascorso è il tuo vero obiettivo di ripristino; finché non lo hai misurato una volta, qualunque numero tu citi è solo una supposizione.

Confronto

Dove mettere la copia

Ogni destinazione messa a confronto con le quattro domande che la determinano: cosa costa alla tua anonimità, cosa costa in denaro, quanto velocemente riavrai i tuoi dati, e a cosa sopravvive.
DestinazioneCosto per l'anonimatoDenaroVelocità di ripristinoSopravvive a
Secondo VPS no-KYC, regione diversaNessuno — pagato dallo stesso saldo in cripto, nessuna identità da nessuna parte$5/mese, dimezzato con un ciclo a 12 mesiVeloce — da datacenter a datacenter su 1–10 GbpsPerdita dell'account, compromissione, una singola giurisdizione, i tuoi stessi errori
Snapshot del providerNessunoEconomicoSecondiQuasi nulla — stesso account, stesso dominio di guasto, muore insieme a entrambi
Storage a oggetti (B2 / S3 / Wasabi)Alto — carta e documento sull'account, e la chiave API sul tuo server punta a essoMolto economico da conservare, le tariffe di egress mordono durante un ripristinoVeloce, se accetti la bolletta dell'egressCompromissione e perdita dell'account, al prezzo di un nome attaccato alla macchina
Provider di storage che accetta criptoBasso, se il percorso di pagamento è genuinamente privo di identità — verifica, non darlo per scontatoModeratoVaria moltissimo — controlla i limiti di egress prima di averne bisognoQuasi tutto, con più rischio di controparte rispetto a un VPS che controlli tu
NAS domestico, pull via WireGuardNessuno — nulla lascia la tua rete non cifrato o non attribuitoHardware che possiedi giàLento — limitato dalla velocità di upload di casa tuaTutto tranne la tua casa; scarso come unica copia, eccellente come terza
Niente / “è dentro git”NessunoGratisMaiNiente. Git conserva il tuo codice; non conserva il tuo database, i tuoi upload o la tua /etc
FAQ

Domande che meritano risposta

Uno snapshot del provider non è già un backup?

No, e la distinzione non è pedanteria. Uno snapshot vive nello stesso account, sotto le stesse credenziali, nello stesso dominio di guasto del server che copia — quindi non sopravvive alla perdita dell'account, e chiunque raggiunga il tuo pannello può cancellarlo insieme all'originale. Viene inoltre catturato da una macchina in esecuzione, il che significa che un database molto attivo viene catturato a metà scrittura. Usa gli snapshot come tasto “annulla” prima di modifiche rischiose; usa un repository cifrato off-site come backup.

restic o Borg — quale dovrei usare?

restic, a meno che tu non abbia una ragione specifica per non farlo. Cifra di default, non richiede nulla installato sulla destinazione, parla ogni backend che valga la pena usare, e viene distribuito come un unico binario statico. Borg deduplica e comprime meglio ed è sensibilmente più veloce su filesystem con milioni di file piccoli, ma deve essere installato e allineato in versione su entrambi i lati ed è pensato davvero per un client per repository. Spool di posta grande ed entrambe le macchine sotto il tuo controllo: Borg. Tutto il resto: restic.

Di quanto disco ha bisogno il target di backup?

Per un repository deduplicato con la scala di retention di questa guida — 7 giornalieri, 5 settimanali, 12 mensili, 2 annuali — metti in conto all'incirca due o tre volte la dimensione dei dati che stai effettivamente mettendo in backup, non dell'intero server. Ventisei snapshot non significano ventisei copie, perché vengono memorizzati di nuovo solo i blocchi cambiati. Un sito piccolo con 15 GB di stato reale ci sta comodamente sullo Starter da 80 GB, con anni di storia d'avanzo.

Un secondo VPS non raddoppia la mia bolletta di hosting?

Solo se il target di backup ha le stesse dimensioni della produzione, e non dovrebbe. Non esegue alcuna applicazione e non serve traffico; ha bisogno di disco, non di CPU. Uno Starter da $5 accanto a un server di produzione da $30 è un sesto della bolletta, e il ciclo a 12 mesi ne toglie un altro 50%. Messo a confronto con il costo di perdere tutto, è la riga più economica in fattura — e il target funge anche da macchina per le esercitazioni di ripristino.

Dove dovrebbe vivere la password del repository?

In un password manager il cui vault è a sua volta salvato altrove, o su carta, o entrambi — e accanto ad essa, l'indirizzo del repository e il comando di ripristino esatto. La copia dentro /etc sul server è una copia di lavoro per l'automazione, mai il verbale ufficiale. Verificala correttamente: da una macchina che non è né la produzione né l'host di backup, usando solo ciò che è nel password manager, esegui restic snapshots. Se serve qualcos'altro, non hai ancora un backup recuperabile.

Con quale frequenza dovrebbero girare i backup?

Chiediti quanto lavoro sei disposto a rifare. Ogni notte va bene per la maggior parte dei server: al massimo si perde un giorno, e un'unica esecuzione notturna è facile da ragionare. Un database molto attivo ne vuole di più — dump orari del solo database, con il passaggio completo sui file che resta notturno, è lo schema abituale. Una frequenza superiore a quel punto rende meno dello stesso sforzo speso in una retention più lunga e in un'esercitazione di ripristino reale, che è dove sta il rischio vero.

Posso fare il backup di un server il cui disco è cifrato con LUKS?

Sì, e i due sono complementari, non ridondanti. LUKS protegge il disco mentre la macchina è spenta; il backup ti protegge dalla cancellazione, dalla corruzione e dalla perdita totale della macchina. Fai il backup del filesystem montato esattamente come faresti con uno non cifrato — restic cifra di nuovo in uscita, quindi il repository è al sicuro anche su storage non fidato. Tieni i due segreti in luoghi genuinamente separati: una passphrase LUKS conservata solo nel repository restic, o una passphrase restic conservata solo dentro il volume LUKS, è un ciclo che si blocca in modo sicuro su entrambi i lati.

Se il mio server viene compromesso, i backup sono al sicuro?

Dipende interamente da una decisione che hai preso mesi prima. Se il server di produzione detiene una credenziale con diritti di cancellazione sul repository, allora no — un attaccante enumera e distrugge prima i backup, perché è ciò che trasforma un incidente in una trattativa. Se il repository è append-only, oppure l'host di backup fa il pull e la produzione non detiene alcuna credenziale di backup, allora la storia sopravvive e ripristini da uno snapshot precedente all'intrusione. Questo è l'intero argomento a favore del progetto pull, ed è il motivo per preferirlo prima di averne bisogno.

Deploy your offshore server.

Scegli una regione. Scegli un piano. Incolla una chiave. Paga. I prossimi 47 secondi sono a nostro carico.