La mascotte orso polare di NordBastion in piedi in una cripta di pietra nordica scura accanto a un rack server, che regge una chiave ciano luminosa, mentre una catena di cinque blocchi olografici ciano contrassegnati da lucchetti si inarca attraverso un arco runico verso un secondo rack server in lontananza, sotto una tenue aurora boreale
Guida pratica · Opsec·16 min di lettura · 45 min pratici

Backup VPS crittografato.
Offsite, deduplicato, e fuori portata.

Backup VPS crittografato in sei passaggi: da un server non protetto a un repository cifrato verificato su una seconda macchina, in una seconda giurisdizione — restic e Borg a confronto, dump del database che si ripristinano puliti, un timer systemd e un repository append-only che un server compromesso non può cancellare. La seconda macchina costa $3.90 al mese. Testato su Debian 12.

I sei passaggi
  1. 01

    Inventario

    Ciò che non può essere ricostruito

  2. 02

    Installare

    restic o Borg

  3. 03

    Destinazione

    Un secondo bastione

  4. 04

    Inizializzare

    Repository + prima esecuzione

  5. 05

    Automatizzare

    Dump, timer, retention

  6. 06

    Esercitazione

    Ripristino, cronometrato

Prima di iniziare · A cosa serve

Quattro modi in cui i dati muoiono. Solo uno di essi è un disco rotto.

Quasi ogni discussione sui backup va storta nel primo minuto, perché le due persone che parlano hanno in mente guasti diversi. Una immagina un disco morto. L'altra immagina un martedì mattina in cui l'account è sparito. Hanno bisogno di risposte diverse, e una configurazione che sopravvive solo al primo caso sembra sicura fino a quando non viene messa alla prova dal secondo.

Ci sono quattro modalità di guasto contro cui vale la pena progettare, e non sono variazioni sullo stesso tema — ciascuna sconfigge una difesa diversa.

Hardware. L'NVMe si guasta, il nodo host muore, l'array perde due membri contemporaneamente. È il fallimento per cui tutti pianificano, ed è il più raro su un'infrastruttura virtualizzata moderna. Lo storage ridondante lo gestisce. Lo storage ridondante non gestisce nient'altro, ed è per questo che il RAID non è un backup e non lo è mai stato: copia fedelmente ogni scrittura, inclusa quella che dice "cancella tutto".

Umano. Uno script di migrazione eseguito contro la produzione. Un DROP TABLE nel terminale sbagliato. Un rsync con gli argomenti invertiti. Questa è di gran lunga la causa più comune di perdita reale di dati, e l'unica difesa è la cronologia — una copia da prima dell'errore, conservata dove l'errore non è arrivato. Il tempo è l'ingrediente: se l'unica copia è uno specchio in ritardo di trenta secondi, contiene già l'errore.

Malevolo. Qualcuno ottiene root, oppure un ransomware entra attraverso un'applicazione non aggiornata. La prima cosa che fa il ransomware moderno è cercare la configurazione dei backup — credenziali, share montate, chiavi cloud — e distruggere ciò che trova, proprio perché un ripristino funzionante trasforma una catastrofe in un pomeriggio. Una credenziale di backup che può cancellare è una credenziale che l'attaccante eredita. È il fallimento per cui esiste il capitolo append-only di questa guida.

Custodiale. Niente si è rotto e niente è stato attaccato; ha semplicemente perso l'accesso. Un account sospeso per un segnale automatico di frode, una carta rifiutata mentre era in viaggio, una contestazione, una verifica di compliance, un'ordinanza notificata al provider. La macchina è intatta e irraggiungibile, e ogni snapshot dentro quell'account è irraggiungibile insieme a lei. Questo è il guasto a cui nessuna quantità di ridondanza dentro un solo provider può rispondere, ed è il motivo per cui un backup serio vive sotto un tetto diverso.

Difesa Hardware Errore umano Ransomware Account perso
RAID / storage ridondanteNoNoNo
Snapshot del provider, stesso accountParzialmenteNoNo
Repository offsite, chiave scrivibileNo
Repository offsite, append-only, altra giurisdizione

L'ultima riga è ciò su cui costruisce il resto di questa guida. Non costa più di quella sopra — l'impostazione append-only è gratuita e la seconda giurisdizione costa gli stessi $3.90 della prima.

La regola 3-2-1, riformulata per un singolo VPS. Tre copie dei dati, su due tipi di storage diversi, una delle quali fuori sede. La formulazione classica risale a un'epoca di nastri e server d'ufficio, e lo spirito sopravvive alla traduzione meglio della lettera. Per una singola macchina self-hosted si legge così: i dati live sul VPS, un repository su una seconda macchina altrove e — per tutto ciò che è davvero insostituibile — una terza copia che può tenere in mano, su un disco a casa o in un cassetto. Il numero che conta davvero non è tre. È quante cose indipendenti devono andare storte prima che i dati siano persi. Due è il minimo che valga la pena avere.

Prima di iniziare · Scelta dello strumento

restic, Borg o rsync. Due di loro sono backup.

La categoria che le serve si chiama strumento di snapshot con deduplicazione e cifratura. Divide ogni file in chunk definiti dal contenuto, cifra ogni chunk sulla macchina che possiede i dati e lo memorizza una sola volta, indipendentemente da quanti snapshot lo referenziano. Questo le dà tre proprietà contemporaneamente: molti punti di ripristino per poco più dello spazio di uno solo, plaintext che non lascia mai la sorgente, e un trasferimento che sposta solo ciò che è cambiato.

Due programmi dominano questo spazio ed entrambi sono eccellenti. restic è un singolo binario statico Go con un'ampia gamma di storage backend. BorgBackup è un programma Python che parla SSH con una copia di se stesso dal lato remoto. Tutto quello che segue è la differenza onesta tra i due.

Proprietà restic BorgBackup rsync / rclone
Cifratura lato clientSempre attivo, non opzionaleSempre attivo, non opzionaleSolo tramite un remote rclone crypt
DeduplicazioneChunk a definizione basata sul contenutoChunk a definizione basata sul contenutoNessuno, o alberi di hardlink
Cronologia degli snapshotSì, con policy di retentionSì, con policy di retentionUno specchio dell'istante presente
Requisiti della destinazioneNiente — SFTP, S3, REST, rcloneBorg installato su entrambi i latiUn account SSH o un'API
Applicazione dell'append-onlyVia rest-server o object lockIntegrato, su semplice SSHNessuno
Adatto aQuasi chiunque, la maggior parte dei backendUna macchina SSH di sua proprietà, append-onlyMirroring, non backup

rsync è in tabella perché è il primo strumento a cui la maggior parte delle persone ricorre, e perché è genuinamente lo strumento sbagliato: un mirror riproduce fedelmente anche una cancellazione, e il mirror di un disco cifrato a riposo non è a sua volta cifrato da nessuna parte. Si guadagna un posto dentro un backup come ciò che sposta un archivio già pronto, non come l'archivio stesso.

Questa guida mostra sia restic che Borg per ogni comando che differisce, così può seguirla con l'uno o con l'altro. Dove è necessario fare una scelta unica — l'esempio pratico, il timer, lo script — usa restic, perché la destinazione a quel punto non ha bisogno di altro che di un account SSH, e questo mantiene la seconda macchina noiosa.

Passo 01 · Inventario

Ciò che non può essere ricostruito. Lo metta per iscritto prima di automatizzarlo.

Eseguire il backup dell'intero filesystem è una scelta difendibile e dispendiosa. Gran parte di un server è a un package manager di distanza dall'essere ricreata, e ogni gigabyte che trascina rende il repository più lento da potare, più costoso da mantenere e più lento da cercare quando ha fretta. L'esercizio utile è il contrario: elenchi ciò che un'installazione pulita non le restituirebbe.

Per un tipico VPS self-hosted quell'elenco è breve, e contiene sempre queste cinque cose:

  • 01Contenuto dei database. Non la directory dati — il dump. Il Passo 05 spiega perché.
  • 02File caricati dagli utenti. Volumi Docker, una directory di upload o media, uno store di posta. Di solito il grosso dei byte.
  • 03Configurazione e segreti. I file Compose, i file .env, le chiavi di cifratura, i token API. Piccoli, e la differenza tra un ripristino di due ore e uno di due giorni.
  • 04Lo stato di sistema che ha modificato. /etc nel suo insieme è abbastanza piccolo da prendere per intero — configurazione sshd, regole del firewall, unit systemd, voci cron, il routing della posta a cui ha dedicato una serata.
  • 05Qualsiasi cosa a cui sia associata un'identità. Una chiave di hidden service Tor, una chiave di firma Matrix, una chiave privata WireGuard, un wallet di nodo. Ne perda una e il servizio non torna — torna un servizio diverso con lo stesso nome.

Trasformi l'elenco in due file sul server sorgente. Una include list mantiene visibile l'intento; una exclude list tiene fuori il rumore. Entrambe vengono lette dal comando di backup, ed entrambe devono far parte del backup stesso.

/etc/backup/include.txt

/etc
/opt
/root
/var/backups
/var/lib/docker/volumes
/home

/etc/backup/exclude.txt

# Caches and rebuildable artefacts
**/node_modules
**/.cache
**/*.tmp
/var/lib/docker/volumes/*/_data/cache
# Log noise — keep the config, drop the volume
/var/log/journal
# Never back up the mount point you restore into
/mnt/restore

Due note su quella riga Docker, perché trae in inganno. I named volume vivono sotto /var/lib/docker/volumes e sono ciò che le serve; i bind mount vivono ovunque li abbia messi, di solito accanto al file Compose, e sono coperti da /opt. Le immagini dei container non sono dati — tornano con un pull, e un backup che le porta con sé sta trasportando gigabyte di cui non ha bisogno.

Infine, misuri il risultato prima di costruirci sopra qualcosa. Il numero le dice quale livello di repository ordinare e se la prima esecuzione richiederà quattro minuti o quaranta:

du -sh --exclude=/var/lib/docker/overlay2 /etc /opt /root /home /var/lib/docker/volumes
Passo 02 · Installazione

Un binario sulla sorgente. Nient'altro cambia.

Installi lo strumento sulla macchina che possiede i dati. Su Debian e Ubuntu entrambi sono pacchettizzati, e per restic il pacchetto della distribuzione è spesso una release o due indietro — il che conta, perché le funzionalità del repository e i miglioramenti di performance arrivano nelle point release. Installi il pacchetto, poi lasci che restic si aggiorni da sé:

apt update && apt install -y restic
restic self-update
restic version

Per Borg, il pacchetto della distribuzione è la scelta giusta, perché la versione sulla sorgente e quella sulla destinazione devono capirsi a vicenda, e una coppia abbinata proveniente dalla stessa release della distribuzione è il modo meno doloroso per ottenerlo:

apt install -y borgbackup
borg --version

Una parola sulle versioni major prima di affidare anni di storia a un repository: il formato del repository di Borg è cambiato tra le sue linee major, e spostare un repository oltre quel confine è una conversione, non un aggiornamento. Legga le note di rilascio della versione che sta installando, e mantenga la destinazione sulla stessa linea major della sorgente. restic è rimasto forward-compatible lungo le sue release e aggiunge funzionalità dietro una versione di repository esplicita, quindi la preoccupazione equivalente è minore.

Nessuno dei due strumenti richiede un daemon, un agente o una porta aperta sulla sorgente. Vale la pena dirlo chiaramente: il sistema di backup che sta installando non aggiunge alcun servizio in ascolto né alcuna nuova superficie d'attacco alla macchina che protegge. Tutto ciò che fa è in uscita, secondo una pianificazione, via SSH.

Passo 03 · Destinazione

Un secondo bastione, e una chiave che non può cancellare. È questo il passo che conta.

L'host del repository ha un solo compito e quasi nessun requisito. Non ha bisogno di core, perché non legge mai i suoi dati — custodisce blob cifrati che non può aprire. Non ha bisogno di memoria, perché il lavoro di deduplicazione avviene sulla sorgente. Ha bisogno di disco, di un indirizzo stabile, e di trovarsi in un posto dove i problemi della macchina sorgente non lo raggiungono.

Nel pannello: Order → VPS → Sentinel, immagine Debian 12, e — il punto centrale — un bastione diverso da quello in cui gira la sorgente. Helsinki al lavoro, Reykjavík che custodisce. Non è richiesto alcun indirizzo email per aprire l'account e la fattura si salda in Monero, Bitcoin, Lightning o uno degli altri asset supportati, il che significa che la seconda macchina non reintroduce l'identità a cui la prima aveva fatto attenzione.

Repository di destinazione Mensile Archiviazione Working set che contiene Costo del ripristino
Sentinel$3.90120 GB NVMe~30 GBNiente — senza limiti di traffico
Garrison$7.90240 GB NVMe~70 GBNiente — senza limiti di traffico
Ravelin$16.90480 GB NVMe~150 GBNiente — senza limiti di traffico
Bulwark$32.90960 GB NVMe~300 GBNiente — senza limiti di traffico
Object storage di classe S3~$0.023/GBElasticoQualsiasiTraffico in uscita a consumo

La colonna del working set presuppone snapshot giornalieri conservati per un anno con un churn normale; un repository che contiene per lo più file che cambiano lentamente arriva molto più lontano, uno pieno di grandi binari riscritti di continuo arriva meno lontano. L'ultima riga serve per la scala, e per il dettaglio che si dimentica fino al giorno sbagliato: l'object storage fa pagare anche il download, ed è il download ciò che si fa in un'emergenza.

Sulla destinazione — un utente non privilegiato e una directory.

adduser --disabled-password --gecos "" backup
install -d -m 0700 -o backup -g backup /srv/restic/web01

L'utente di backup non ha password, non ha sudo e non ha nulla in cui accedere. Esiste solo per possedere una directory. Gli assegni una directory propria per ogni macchina sorgente se ne fa il backup di più d'una — un repository per sorgente impedisce che la compromissione di una macchina tocchi la storia di un'altra.

Sulla sorgente — una chiave usata per nient'altro.

ssh-keygen -t ed25519 -f /root/.ssh/id_backup -N "" -C "backup:web01"
cat /root/.ssh/id_backup.pub

Non riutilizzi la sua chiave di login. Questa vive non cifrata su disco perché un timer non presidiato deve usarla alle tre del mattino, ed è esattamente il tipo di chiave che vuole limitata a una singola destinazione e a un singolo scopo.

Di nuovo sulla destinazione — limiti cosa quella chiave può fare. Incolli la chiave pubblica in /home/backup/.ssh/authorized_keys con un prefisso davanti. Per restic via SFTP:

restrict,from="198.51.100.7" ssh-ed25519 AAAAC3NzaC1lZDI1... backup:web01

L'opzione restrict disattiva in una sola parola il port forwarding, l'agent forwarding, X11 e l'allocazione PTY, lasciando solo il trasferimento file e nient'altro. La clausola from= vincola la chiave all'indirizzo sorgente, quindi una sua copia rubata dal server è inutile altrove. Per Borg, faccia un passo in più e forzi il comando, dove risiede la sua modalità append-only:

command="borg serve --append-only --restrict-to-path /srv/borg/web01",restrict ssh-ed25519 AAAAC3NzaC1lZDI1... borg:web01

Quella riga è tutta la difesa anti-ransomware in un unico posto: qualunque cosa invii la sorgente, la destinazione eseguirà solo borg serve, solo dentro quel percorso, e solo in una modalità che aggiunge. Una shell root sulla sorgente non può usare questa chiave per cancellare il mese scorso.

Nomini la destinazione una volta sola, sulla sorgente. Lo inserisca in /root/.ssh/config così ogni comando successivo è breve e l'indirizzo vive in un unico file:

Host rkv-repo
    HostName 198.51.100.42
    User backup
    IdentityFile /root/.ssh/id_backup
    IdentitiesOnly yes

Poi si connetta una volta a mano — ssh rkv-repo — per accettare la host key. Un timer non presidiato non risponderà mai a un prompt di fingerprint, e un primo backup che resta bloccato in silenzio per una settimana è un classico. Mentre è sulla destinazione, la tratti come qualsiasi altra macchina che possiede: esegua anche lì la checklist di hardening della prima ora. Custodisce una copia di tutto, e merita l'ora intera.

Passo 04 · Inizializzazione

La passphrase, poi il primo snapshot. In quest'ordine, e fuori dalla macchina.

Generi una passphrase che non digiterà mai. Viene letta da un file da uno script, quindi la lunghezza non costa nulla e non c'è motivo di renderla memorizzabile:

openssl rand -base64 32 > /root/.restic-pass
chmod 600 /root/.restic-pass
cat /root/.restic-pass

Ora si fermi e copi quella stringa da qualche parte che non sia questo server. Un password manager, un foglietto di carta in un cassetto, una seconda macchina — un posto qualsiasi che sopravviva alla perdita della sorgente. È lo stesso fallimento che distrugge i vault self-hosted e i motori di automazione: la chiave che decifra tutto è conservata accanto a tutto, ed entrambi si perdono insieme. Un repository la cui passphrase esisteva solo sulla macchina che proteggeva non è un backup; è un mucchio di rumore molto ordinato.

Scriva il file di environment che ogni comando successivo leggerà:

/etc/backup/restic.env

RESTIC_REPOSITORY=sftp:rkv-repo:/srv/restic/web01
RESTIC_PASSWORD_FILE=/root/.restic-pass
chmod 600 /etc/backup/restic.env
set -a; . /etc/backup/restic.env; set +a
restic init

La coppia equivalente per Borg — la modalità repokey memorizza la chiave di cifratura dentro il repository, protetta dalla passphrase, il che è comodo ed è esattamente il motivo per cui ne esporta anche una copia:

export BORG_REPO=ssh://borg@rkv-repo/srv/borg/web01
export BORG_PASSCOMMAND="cat /root/.restic-pass"
borg init --encryption=repokey-blake2
borg key export ::  /root/borg-key.txt   # copy this off the server too

Il primo snapshot. Lo esegua a mano, lo osservi e lo lasci terminare prima di automatizzare qualsiasi cosa. Questa è l'esecuzione lenta — ogni chunk è nuovo — ed è quella che le dice se la include list era corretta:

restic backup \
  --files-from /etc/backup/include.txt \
  --exclude-file /etc/backup/exclude.txt \
  --tag nightly --one-file-system --verbose

Se la sorgente sta servendo traffico e l'upload sta saturando il collegamento, lo limiti. Il flag accetta kibibyte al secondo, quindi 20000 corrisponde a circa 20 MB/s:

restic backup --limit-upload 20000 --files-from /etc/backup/include.txt

Quando finisce, guardi cosa ha ottenuto realmente. Questi tre comandi sono quelli da eseguire ora, e di nuovo tra sei mesi, quando avrà smesso di pensarci:

restic snapshots                    # the list, with dates and tags
restic stats latest                 # what the newest snapshot contains
restic ls latest /etc/backup        # confirm a path you expected is in there

Quel terzo comando merita un momento di attenzione. Metà dei backup rotti non sono affatto rotti — sono repository completi e sani della directory sbagliata. Elencare un percorso che si aspettava di trovare, già al primissimo snapshot, intercetta il problema mentre l'include file è ancora fresco nella sua mente.

Passo 05 · Automazione

Prima il dump, poi lo snapshot. Uno script, un timer, una politica di retention.

La regola del database, detta una volta sola. Un motore di database in esecuzione mantiene lo stato in memoria e lo scrive su disco secondo i propri tempi. Legga i suoi file mentre lo sta facendo e ottiene un insieme di pagine provenienti da momenti diversi — un file che può ripristinarsi, può ripristinarsi corrotto, o può ripristinarsi in qualcosa che si apre correttamente e manca silenziosamente dell'ultima ora. Non c'è modo di distinguerli dall'esterno. Per questo il backup non tocca mai i file live: un dump viene scritto su disco per primo, e il backup preleva il dump.

Tutto finisce in un unico script. Lo metta in /usr/local/sbin/nb-backup.sh, lo renda eseguibile, e lo mantenga anche nel backup stesso:

#!/bin/sh
set -eu

# ---- 1. Consistent dumps, written where the include list will find them.
install -d -m 0700 /var/backups/db

# PostgreSQL in a container:
docker exec -t app-db pg_dumpall -U postgres | gzip -9 > /var/backups/db/postgres.sql.gz

# MariaDB / MySQL, InnoDB tables:
# docker exec -t mail-db mariadb-dump --single-transaction --all-databases \
#   | gzip -9 > /var/backups/db/mariadb.sql.gz

# SQLite — never a plain file copy:
# sqlite3 /opt/app/data/app.db ".backup '/var/backups/db/app.db'"

# ---- 2. The snapshot.
restic backup \
  --files-from /etc/backup/include.txt \
  --exclude-file /etc/backup/exclude.txt \
  --tag nightly --one-file-system

# ---- 3. Retention, then reclaim the space it freed.
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

# ---- 4. Structural check every night; read 5% of the data on Sundays.
restic check
[ "$(date +%u)" = "7" ] && restic check --read-data-subset=5%

echo "backup ok: $(date -u +%FT%TZ)"

Tre dettagli in quello script lavorano più di quanto sembri. Il set -eu in cima significa che un dump fallito interrompe l'esecuzione invece di fare silenziosamente lo snapshot del dump di ieri per i successivi sei mesi. Il forget e il prune sono un unico comando, perché una retention policy che non recupera mai spazio è un disco che si riempie. E il controllo della domenica legge una porzione dei dati reali anziché solo l'indice — la corruzione di un repository è rara, ed è silenziosa, e l'unica cosa che la scopre è la lettura.

Il timer. Due piccoli file unit. systemd è lo scheduler giusto qui, piuttosto che cron, perché un timer offre Persistent — un'esecuzione mancata mentre la macchina era spenta avviene al boot successivo invece di essere saltata in silenzio — e perché journalctl conserva poi la cronologia di ogni esecuzione.

/etc/systemd/system/nb-backup.service

[Unit]
Description=Nightly encrypted offsite backup
Wants=network-online.target
After=network-online.target docker.service

[Service]
Type=oneshot
EnvironmentFile=/etc/backup/restic.env
ExecStart=/usr/local/sbin/nb-backup.sh
Nice=10
IOSchedulingClass=idle

/etc/systemd/system/nb-backup.timer

[Unit]
Description=Nightly encrypted offsite backup

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=45m
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now nb-backup.timer
systemctl list-timers nb-backup.timer
systemctl start nb-backup.service && journalctl -u nb-backup.service -n 40 --no-pager

Il ritardo casuale non è un vezzo. Ogni server self-hosted su internet esegue la propria manutenzione esattamente alle 03:00, e un backup che parte nello stesso secondo della rotazione dei log, del rinnovo del certificato e dell'aggiornamento dei container passa la notte a contendere I/O con loro. Distribuire l'avvio su quarantacinque minuti non costa nulla ed elimina un'intera categoria di notti misteriosamente lente.

Poi renda il fallimento rumoroso. Questo è il passaggio che tutti saltano ed è quello che decide se tutto il resto è servito a qualcosa. Un backup che smette di funzionare non lo annuncia: il timer continua a scattare, il servizio continua a fallire, e l'ultimo snapshot valido retrocede sempre più nel passato mentre la dashboard resta verde. Aggiunga una unit OnFailure= al servizio che le invii qualcosa che vedrà davvero, e una volta al mese dia un'occhiata all'inizio della lista degli snapshot. Una riga, un'abitudine.

restic snapshots --latest 3        # is the newest one from last night?
systemctl list-timers nb-backup.timer   # is the timer still armed?
Passo 06 · Esercitazione

Lo ripristini ora, mentre nulla sta bruciando. Un backup non testato è una voce di corridoio.

Ci sono due esercitazioni, e rispondono a domande diverse. Quella piccola chiede "i dati sono davvero lì e leggibili?" e richiede novanta secondi. Quella grande chiede "quanto tempo servirebbe davvero per far tornare online il servizio?" e richiede un pomeriggio, una volta all'anno. Faccia quella piccola ogni mese. Faccia quella grande almeno una volta, perché la risposta non è mai quella che ci si aspetta.

L'esercitazione piccola. Recuperi una directory e un dump in un percorso temporaneo, poi li confronti con ciò che è in produzione:

install -d -m 0700 /mnt/restore
restic restore latest --target /mnt/restore --include /etc/backup
restic restore latest --target /mnt/restore --include /var/backups/db

diff -r /etc/backup /mnt/restore/etc/backup && echo "config identical"
gzip -t /mnt/restore/var/backups/db/postgres.sql.gz && echo "dump readable"

Ancora meglio, quando lo strumento lo offre: monti il repository in sola lettura e lo percorra come un filesystem. Ogni snapshot appare come una directory datata, e può sfogliare la storia con ls e cat senza ripristinare nulla. Richiede FUSE sulla macchina che esegue il mount:

restic mount /mnt/snapshots      # then: ls /mnt/snapshots/snapshots/latest/
borg mount ::  /mnt/snapshots     # the Borg equivalent

L'esercitazione grande. Noleggi un VPS usa e getta a ore, e ricostruisca il servizio partendo solo dal repository — nessuna nota a memoria, nessun file copiato dalla macchina live. Lavori da un runbook scritto e lo corregga strada facendo, perché le lacune che trova sono proprio lo scopo dell'esercizio. Le scoperte più frequenti, in ordine: la passphrase era solo sulla macchina morta; i record DNS non erano mai stati scritti da nessuna parte; una directory bind-mounted si trovava fuori da ogni include path; il ripristino richiedeva una versione del pacchetto che non è più quella predefinita; nessuno sapeva quale container dovesse partire per primo.

Lo cronometri. Scriva il numero in cima al runbook insieme alla data. Quel numero — non la dimensione del repository, non il numero di snapshot — è l'unica misura onesta di quanto sia davvero protetto il servizio.

E legga i dati di tanto in tanto. Il controllo notturno verifica la struttura: che l'indice sia coerente con se stesso e che non manchi nessun blob. Non legge i blob. Una volta al mese, oppure nell'esecuzione della domenica, ne legga una porzione — un cinque per cento a rotazione copre l'intero repository nel giro di un paio di mesi senza mai costare una notte lunga:

restic check --read-data-subset=5%
borg check --verify-data              # the Borg equivalent, slower and thorough
La parte difficile · Append-only

Una chiave che può scrivere, e non può cancellare. La differenza tra una brutta settimana e un'attività chiusa.

Tutto quanto fin qui protegge dagli incidenti. Questo capitolo protegge da un avversario, e il problema di progettazione è scomodo una volta che lo si vede: la macchina sorgente deve raggiungere il repository ogni notte, il che significa che la macchina sorgente detiene una credenziale funzionante per il repository. Chi possiede la sorgente possiede quella credenziale. Se la credenziale può cancellare, l'attaccante cancella — e il ransomware moderno fa esattamente questo, di proposito, prima di cifrare qualsiasi cosa, perché una vittima che può ripristinare non paga.

La soluzione non è una password migliore. È un permesso asimmetrico: la sorgente può aggiungere dati e non può rimuoverne nessuno. Tre modi per costruirlo, in ordine decrescente di facilità con cui riuscire a farlo bene.

Uno — append-only forzato via SSH. Questo è il terreno di casa di Borg ed è la risposta più pulita disponibile su una macchina che possiede. La voce in authorized_keys del passo 03 forza borg serve --append-only, quindi il lato remoto rifiuta le cancellazioni qualunque cosa richieda il lato vicino. Il pruning deve comunque avvenire, ma avviene dal lato della destinazione, secondo una pianificazione che la sorgente non può influenzare, eseguita da un account che la sorgente non può raggiungere. Un attaccante con root sulla sorgente può riempire il repository di spazzatura; non può rimuovere l'archivio della notte precedente al suo arrivo.

Due — un endpoint REST append-only. Il rest-server, il compagno di restic, ha una modalità --append-only con la stessa proprietà: i nuovi blob vengono accettati, quelli esistenti non possono essere rimossi. Lo esegua sulla destinazione dietro TLS, punti RESTIC_REPOSITORY all'URL https:// invece che a quello sftp:, e la forma della difesa è identica. Costa un servizio in più sulla destinazione e compra la stessa garanzia.

Tre — object lock. Su storage compatibile S3, il versioning più un periodo di retention con object-lock rende la cancellazione impossibile fino alla scadenza del periodo, applicato dal livello di storage anziché da un programma. È il più forte dei tre e il più costoso, e introduce un account presso un provider — il che riporta indietro il rischio custodiale che l'intera guida cerca di distribuire. Sensato come terza copia, scomodo come unica.

Se nessuna di queste soluzioni si adatta — SFTP semplice, una chiave che può scrivere e quindi cancellare — non finga il contrario; aggiunga una seconda copia indipendente che la sorgente non può toccare affatto. Pull invece di push: lasci che la destinazione effettui il login sulla sorgente, legga ciò che le serve, e lo conservi. Le credenziali vivono allora sulla macchina che non è esposta, e un attaccante sulla sorgente non trova nulla che punti al backup. Richiede un po' più di lavoro da configurare e inverte il rischio esattamente nella direzione giusta.

Qualunque scelga, lo verifichi come verificherebbe qualsiasi altro controllo di sicurezza — cercando di romperlo. Dalla sorgente, con la chiave di backup, tenti una cancellazione. Il risultato corretto è un rifiuto.

borg delete ::name-of-an-old-archive
# → Remote: Repository is in append-only mode. Refusing to delete.
Note dal campo · Per servizio

Il file senza il quale ogni servizio non può tornare in funzione. Raramente è quello grande.

Ogni servizio ha un piccolo pezzo di stato che non è dato e non è configurazione, e perderlo non danneggia il servizio — lo sostituisce con un servizio diverso che per caso ha lo stesso nome. I vecchi link si rompono, i peer federati rifiutano la nuova identità, i client rigenerano le chiavi. Questi sono i pezzi, per stack, per le cose per cui questo sito ha delle guide.

Il livello sotto lo storage

La distanza è una risposta tecnica. La giurisdizione è l'altra metà.

Chieda alla maggior parte delle persone perché il backup dovrebbe stare altrove e la risposta sarà incendio, allagamento, o un datacenter che perde corrente. Tutto vero, tutto sempre più raro, e tutto risolto da cento chilometri di distanza. I guasti che nel 2026 mandano davvero in crisi le attività sono amministrativi, e la sola distanza non fa nulla contro di essi.

Un account è un singolo punto di guasto. Una sospensione per un segnale automatico di frode, un chargeback mentre era in aereo, una verifica di compliance, una takedown indirizzata alla società anziché a una macchina — ognuna di queste raggiunge ogni server sotto quell'account nello stesso momento, incluso quello che detiene gli snapshot. La ridondanza tecnica era perfetta e irrilevante. Due provider, o come minimo due account sotto due tetti legali diversi, è l'unica struttura che risponde a questo.

Una giurisdizione è un solo quadro legale. I quattro bastioni nordici non sono intercambiabili — Svezia, Finlandia, Norvegia e Islanda proteggono ciascuna qualcosa di leggermente diverso, e Norvegia e Islanda si trovano interamente fuori dall'Unione Europea. Distribuire una macchina di lavoro e il suo repository tra due di esse significa che la copia non si trova semplicemente altrove; è sottoposta a un secondo insieme di regole, indipendente. Il riferimento sulle giurisdizioni nordiche ripercorre gli statuti uno per uno.

E la destinazione non impara nulla. Questo è ciò che rende la separazione economica anziché un compromesso. Sia restic che Borg cifrano prima che qualsiasi cosa lasci la sorgente, quindi l'host del repository memorizza blob per cui non ha la chiave. Non sa quali file custodisce, né quanti, né come si chiamano. Non sta estendendo fiducia a una seconda parte; sta noleggiando un disco incapace di leggere se stesso. È anche la risposta onesta a "dovrei usare come destinazione di backup un provider di cui non mi fido del tutto?" — per un repository cifrato, la fiducia c'entra ben poco.

La latenza non è un fattore. Da Helsinki a Reykjavík sono 30 ms sulla dorsale, da Helsinki a Stockholm 8 ms, da Stockholm a Oslo 11 ms. Un backup notturno non si cura di nessuno di questi numeri, e nemmeno un ripristino limitato dal throughput del disco. Scelga la coppia per la distanza legale, non per la distanza di rete — la distanza di rete tra i paesi nordici è un errore di arrotondamento.

Un'ultima cosa che vale la pena dire chiaramente, perché è il motivo per cui questa guida sta su questo sito e non su un generico blog da sysadmin: la seconda macchina riapre ogni domanda a cui la prima aveva già risposto. Se il VPS di lavoro è stato ordinato senza documento d'identità e pagato in Monero, e poi la macchina di backup viene ordinata con una carta aziendale e una scansione del passaporto, la coppia è identificabile esattamente quanto la metà più debole. La guida pilastro sull'hosting VPS anonimo percorre i tre livelli — iscrizione, pagamento, rete — e si applicano a una destinazione di backup esattamente come a un server web.

Note dal campo · Sei trappole

Sei modi in cui un backup si rivela non esserlo. Tutti scoperti nel giorno sbagliato.

Trappola 01 · Irreversibile

La passphrase era solo sulla macchina morta

Il repository è intatto, cifrato e permanentemente illeggibile. Conservi la passphrase — e per Borg la chiave esportata — in un password manager e su carta, prima del primo snapshot.

Trappola 02 · Coerenza

Il database si ripristina, poi fallisce alla prima query

I file di dati live sono stati copiati invece di essere dumpati. Scriva un dump in un passo pre-backup e faccia il backup del dump; non punti mai l'elenco include alla directory dati di un motore in esecuzione.

Trappola 03 · Raggio d'impatto

L'intruso ha cancellato prima i backup

Una credenziale scrivibile su una macchina compromessa è una credenziale scrivibile per l'attaccante. Forzi l'append-only sulla destinazione, oppure ottenga la seconda copia per pull invece che per push.

Trappola 04 · Capacità

Il repository è più grande del server

forget senza prune segna gli snapshot e non recupera nulla. Li esegua insieme, mantenga spazio libero sulla destinazione, e non faccia mai il backup delle immagini dei container o di node_modules.

Trappola 05 · Silenzio

L'ultimo snapshot valido risale a cinque mesi fa

Il timer ha smesso di funzionare dopo un upgrade e nulla lo ha segnalato. Aggiunga una unit OnFailure= e controlli l'inizio della lista degli snapshot una volta al mese — bastano dieci secondi.

Trappola 06 · Ambito

Tutto è stato ripristinato tranne quella directory

Un bind mount fuori da /opt, un volume spostato durante una migrazione, un pattern di esclusione che ha corrisposto più del previsto. Elenchi un percorso noto a ogni nuovo snapshot, e rilegga il file include dopo ogni modifica allo stack.

FAQ · Backup VPS

Domande, con risposta.

Dieci domande che emergono durante la configurazione — e due che emergono solo dopo, una volta sola.

Uno snapshot del provider è un backup?

No — è un rollback, e utile, ma fallisce esattamente nei momenti per cui esiste un backup. Uno snapshot vive dentro lo stesso account del provider del server che copia. Se l'account viene sospeso, se un pagamento viene contestato, se un agente del supporto sbaglia un clic e cancella, o se un attaccante ottiene le sue credenziali del pannello, lo snapshot se ne va con la macchina. Inoltre non offre nulla di granulare: non può recuperare un singolo file cancellato di ieri, solo riportare indietro l'intero disco e perdere tutto quanto successivo. Mantenga gli snapshot — sono il modo più veloce per annullare un aggiornamento andato male — ma non li confonda con una copia che esiste in un posto che il problema originale non può raggiungere. La regola pratica: se una credenziale compromessa può distruggere entrambe le copie, lei ha una copia sola.

restic o Borg: quale dovrei usare davvero?

Entrambi cifrano lato client, entrambi deduplicano a livello di chunk, entrambi sono maturi, ed entrambi svolgono il compito. Scelga restic se vuole un singolo binario statico, un repository che può vivere su SFTP, object storage compatibile S3, un server REST o qualsiasi cosa rclone possa raggiungere, e nulla installato sulla destinazione. Scelga Borg se la destinazione è una macchina SSH che controlla, vuole l'applicazione dell'append-only più forte disponibile senza storage con object-lock, e apprezza la sua compressione leggermente più stretta. Le differenze pratiche che decidono: Borg richiede borg installato su entrambi i lati e parla solo SSH o un percorso locale; restic richiede un passaggio di prune che tiene brevemente un lock esclusivo sul repository. Se non riesce a decidere, usi restic — meno parti in movimento sulla destinazione vale più di qualsiasi benchmark.

Quanto spazio su disco serve alla macchina di backup?

Parta dalla dimensione di ciò che sta effettivamente sottoponendo a backup — non dalla dimensione del disco su cui risiede. Uno stack self-hosted tipico è qualche gigabyte di database e configurazione più tutto ciò che gli utenti hanno caricato. Deduplicazione e compressione fanno poi il loro lavoro: uno snapshot giornaliero di un working set di 20 GB con una retention di dodici mesi si assesta di solito tra 40 e 80 GB di repository, perché solo i chunk modificati vengono mai memorizzati due volte. Un Sentinel ($3.90/mese, 120 GB NVMe) copre questo caso comodamente. Passi a un Garrison (240 GB, $7.90) quando più server fanno backup sullo stesso host repository, e a un Bulwark (960 GB, $32.90) quando il working set stesso arriva a centinaia di gigabyte. Dimensioni per il repository, poi raddoppi — un repository senza spazio libero non può fare prune, e un repository che non può fare prune può solo crescere.

Posso fare il backup di un database live copiandone i file?

Può copiarli. Potrebbe non riuscire a ripristinarli. Un database che scrive su disco mentre lei lo legge le consegna un insieme di file provenienti da diversi istanti, il che è la definizione di un backup strappato — si ripristina come un database corrotto, o peggio, come un database che si apre correttamente e a cui mancano silenziosamente delle righe. La soluzione è un dump: pg_dump o pg_dumpall per PostgreSQL, mariadb-dump --single-transaction per MariaDB e MySQL su tabelle InnoDB, e sqlite3 db .backup out.db o VACUUM INTO per SQLite. Scriva il dump su un file, poi faccia il backup del file. Se il database è troppo grande per essere dumpato ogni notte, le alternative sono uno snapshot del filesystem preso mentre il motore è brevemente sospeso, oppure lo strumento di backup fisico nativo del motore — ma per tutto ciò che gira su un singolo VPS, un dump è la scelta giusta ed è semplice.

Cosa succede se perdo la passphrase del repository di backup?

I backup sono persi. Sia restic che Borg cifrano lato client, e nessuno dei due offre un percorso di recupero, una master key o un ticket di supporto che le sblocchi un repository. È proprio questo il punto: l'host di destinazione — incluso un host che non è suo — non vede mai il suo plaintext. Significa anche che la passphrase è ora un dato che vale esattamente quanto tutto ciò che protegge, e non deve trovarsi solo sulla macchina di cui si fa il backup. La conservi nel suo password manager, e ne tenga una copia in un luogo fisico. Per Borg, esporti anche la chiave del repository con borg key export e la conservi a parte; in modalità repokey la chiave vive dentro il repository, quindi un repository che non è più raggiungibile si porta via anche la chiave.

Come impedire a un server compromesso di cancellare i propri backup?

Le assegni una credenziale che può aggiungere ma non rimuovere. È la decisione di design più importante di tutto l'esercizio, perché il ransomware moderno cerca prima di tutto la configurazione dei backup e la segue fino in fondo. Tre modi per ottenerla. Append-only via SSH: nell'authorized_keys della destinazione, forzi il comando borg serve --append-only --restrict-to-path /srv/borg — la sorgente può scrivere nuovi archivi e non può cancellare quelli vecchi. Append-only via REST: esegua il rest-server di restic con --append-only e punti il repository verso di esso via HTTPS. Object lock: un bucket compatibile S3 con versioning e un retention lock. Il semplice SFTP non le offre nulla di tutto ciò — una chiave che può scrivere un repository può cancellarlo — quindi se SFTP è il suo trasporto, lo abbini a una seconda copia di tipo pull presa dal lato destinazione, dove le credenziali che un attaccante ha catturato sulla sorgente non arrivano.

Ogni quanto vanno eseguiti i backup, e per quanto tempo vanno conservati?

Se lo chieda al contrario: quanto lavoro è disposto a rifare? Quel numero è il suo recovery point objective, ed è quello a fissare l'intervallo. Ogni notte va bene per quasi ogni servizio self-hosted — un mail server, un Nextcloud, un homeserver Matrix, un motore di workflow. Ogni ora vale la pena quando i dati sono transazionali e il reinserimento è impossibile. Per la retention, lo schema che sopravvive al contatto con la realtà è --keep-daily 7 --keep-weekly 4 --keep-monthly 12: una settimana di undo granulare, un mese di checkpoint settimanali, un anno di checkpoint mensili, per circa 23 snapshot. La coda lunga conta più di quanto ci si aspetti, perché il guasto che intercetta non è un disco morto — è una corruzione o una cancellazione che nessuno ha notato per sei settimane.

La seconda copia deve davvero trovarsi in un paese diverso?

Un edificio diverso è il minimo tecnico; una giurisdizione diversa è la parte che quasi tutti saltano e poi rimpiangono. Un incendio, un allagamento o un guasto a livello di rack sono risolti dalla sola distanza. Ciò a cui la distanza non risponde è il piano legale o commerciale: un account sospeso da un provider, una contestazione di pagamento, una takedown che raggiunge ogni macchina sotto lo stesso tetto societario, un'ordinanza notificata a una società. Se entrambe le copie vivono presso lo stesso provider, una sola lettera le raggiunge entrambe. Dividere la coppia tra due bastioni nordici — la macchina di lavoro a Helsinki, il repository a Reykjavík, 30 ms di distanza sulla dorsale — non costa nulla in latenza e compra un secondo quadro legale. I dati sono comunque cifrati prima di lasciare la sorgente, quindi la destinazione non impara nulla nel conservarli.

Quanto tempo richiede il primo backup, e la cifratura lo rallenta?

La cifratura non è il collo di bottiglia — le CPU moderne eseguono l'AES più velocemente di quanto un collegamento a un gigabit possa trasportarne il risultato, ed entrambi gli strumenti usano l'accelerazione hardware dove esiste. Il collo di bottiglia è il primo upload, perché tutto è nuovo: su un uplink da 1 Gbps un working set di 20 GB richiede pochi minuti a velocità di linea, e piuttosto di più se la destinazione è limitata o i file sono numerosi e piccoli. Ogni esecuzione successiva trasferisce solo i chunk cambiati, che per uno stack tipico sono decine di megabyte e termina in meno di un minuto. Se la prima esecuzione entra in conflitto con un carico di produzione, la limiti — restic accetta --limit-upload in KiB/s, Borg accetta --upload-ratelimit — e la esegua sotto nice e ionice.

Devo davvero testare i ripristini?

Sì, e il motivo non è paranoia — è che i guasti comuni sono silenziosi. Il timer che ha smesso di scattare dopo un aggiornamento di pacchetto. Il pattern exclude che ha silenziosamente inghiottito la directory degli upload. Il dump del database scritto in un percorso che il backup non ha mai coperto. Il repository che fallisce il controllo di integrità da settimane, in un log che nessuno legge. Nessuno di questi si annuncia da solo; tutti si scoprono in novanta secondi ripristinando un file e guardandolo. Faccia un piccolo ripristino ogni mese — recuperi un dump del database e una directory di dati in un percorso temporaneo e li confronti — e una ricostruzione completa una volta all'anno, su un VPS nuovo, cronometrata. Il numero che esce da quella ricostruzione è il suo vero tempo di recupero, e raramente è il numero che avrebbe immaginato.

Ottenere la seconda macchina

Un bastione per il repository in una seconda giurisdizione nordica. KYC-free, pagamento in crypto.

Sentinel — 120 GB di NVMe a $3.90 al mese, a Helsinki, Stockholm, Oslo o Reykjavík. Banda illimitata, quindi ripristinare non costa nulla. Nessuna email in fase di registrazione, nessun documento d'identità, e una destinazione incapace di leggere un solo byte di ciò che custodisce.

Ultima revisione · 2026-08-24 · Fonti · documentazione di restic e BorgBackup, manuali di backup di PostgreSQL e MariaDB, sshd authorized_keys(5), systemd.timer(5) · Cadenza · annuale