Backup (GFS su R2)
La CLI Flo implementa un sistema di backup Grandfather-Father-Son (GFS) per ogni tenant: dump PostgreSQL (opzionalmente archivi completi) caricati sul bucket Cloudflare R2 del tenant, con retention applicata dalle regole di lifecycle del bucket.
R2 bucket (flo-<slug>-backups)
├── daily/ → conservati 7 giorni
├── weekly/ → conservati 30 giorni (domeniche)
├── monthly/ → conservati 90 giorni (primo del mese)
└── sidecar <artifact>.sha256
Cosa contiene un backup
| Forma | Contenuti |
|---|---|
flo backup create <id> | pg_dump --clean --if-exists compresso con gzip → flo-<timestamp>.sql.gz |
flo backup create <id> --full | Il dump DB più un archivio flo-<timestamp>-full.tar.gz della directory tenant (upload, certificati, .env) — backups/ e .flo-private/ sono esclusi. I media R2 sono prima scaricati e impacchettati come r2-media/ |
Gli archivi completi sono cifrati in place con AES-256-GCM prima dell'upload. Il layout file
è FLOBK1 | salt(32) | iv(16) | ciphertext | authTag(16); la chiave è
derivata con scrypt dal ~/.flo/master.key dell'operatore. Il ripristino rileva automaticamente
l'header magico e decifra in modo trasparente.
Ogni artefatto prodotto riceve un sidecar <artifact>.sha256 (formato sha256sum coreutils),
scritto prima dell'upload e copiato in ogni livello insieme all'artefatto.
Il ripristino lo verifica; --require-checksum trasforma un sidecar mancante in un
errore bloccante.
Pianificazione
flo backup schedule <id> --bucket flo-<slug>-backups --cron "0 3 * * *" -y
flo backup schedule <id> --run-now -y # pianificazione + un'esecuzione immediata di verifica
flo backup schedule <id> --disable # rimuove la pianificazione
- Cron predefinito:
0 3 * * *(giornaliero alle 03:00). Il comando scrive~/.flo/scripts/flo-backup-<id>.shsull'host di destinazione e accoda una voce crontab etichettata, con log in~/.flo/logs/flo-backup-<id>.log. Le riesecuzioni sono idempotenti — i duplicati sono consolidati e una guardia anti-doppio-cron ricontrolla il crontab dopo l'installazione. - Il bucket di destinazione è creato se mancante e verificato raggiungibile prima che la pianificazione sia installata, così un'esecuzione notturna non può mai scrivere nel vuoto.
- I livelli GFS sono calcolati per esecuzione: daily sempre, weekly la domenica, monthly il primo del mese (una domenica primo del mese prende tutti e tre).
- Sui tenant di produzione (
isTest !== true) il comando di pianificazione applica anche automaticamente il lifecycle canonico 7/30/90; i cloni di test conservano copie notturne ma nessun lifecycle salvo applicazione manuale.
La forma remota del control-plane accetta --bucket e --cron (validati), con
--run-now obbligatorio; set-bucket e retention girano lato operatore con --vps
perché le chiavi R2 di scrittura restano fuori dal control plane.
Retention
La retention è applicata solo dalle regole di lifecycle del bucket — lo script di backup non elimina mai nulla.
flo backup retention [id] # valutazione in sola lettura di ogni bucket (7/30/90)
flo backup retention <id> --apply # scrive il lifecycle GFS canonico
Le regole canoniche sono daily/ → 7d, weekly/ → 30d, monthly/ → 90d, più
abort degli upload multipart incompleti dopo 7 giorni. --apply richiede un ID istanza esplicito
(nessuna scrittura massiva) e una conferma (o -y).
Riferimento comandi
| Comando | Descrizione | Opzioni principali |
|---|---|---|
flo backup create <id> | Crea un dump DB | --full, --upload, --s3-* |
flo backup restore <id> | Ripristina un backup DB (o completo) | --file, --from-local, --fresh, --no-owner, --no-restart, --require-checksum, -y |
flo backup ls <id> | Elenca backup locali + S3 con dimensione e data | --s3-* |
flo backup download <id> | Scarica da S3 | --file <key>, --from-vps <profile>, --s3-* |
flo backup upload <id> | Carica un file locale su S3 | --file <path>, --s3-* |
flo backup status [id] | Freschezza backup (R2) + salute cron, uno o tutti i tenant | --json |
flo backup retention [id] | Legge/applica il lifecycle GFS del bucket | --apply, -y |
flo backup schedule <id> | Installa/rimuove il cron GFS | --bucket, --cron, --run-now, --disable, -y |
flo backup set-bucket <id> <bucket> | Collega un bucket all'istanza |
Le credenziali S3 si risolvono dai flag --s3-*, dalle env var FLO_S3_* o dal
config.r2 memorizzato; il bucket da --s3-bucket, FLO_S3_BUCKET o
tenant.backupBucket. flo backup download senza --file apre un selettore interattivo
ordinato per nome file/data (dal più recente), non per prefisso di livello.
Dove risiedono i file
- Locale (host di destinazione):
~/.flo/instances/<id>/backups/—flo-<ts>.sql.gz,flo-<ts>-full.tar.gz, i rispettivi sidecar.sha256e gli snapshot pre-ripristino. - R2:
<bucket>/daily/,<bucket>/weekly/,<bucket>/monthly/, con gli oggetti.sha256corrispondenti accanto a ogni artefatto.
Verifica
flo backup status # ogni istanza nelle location risolte
flo backup status <id> # una istanza; esce non-zero in caso di fallimento
flo backup ls <id> # quali oggetti esistono davvero
flo backup retention # la retention da lifecycle è davvero applicata?
status risponde a due domande insieme: esiste un dump fresco in R2 (probe reale,
incluso il rilevamento del bucket media), ed è pianificato esattamente un cron di backup. La
scheda Backup della dashboard del control-plane mostra la stessa freschezza tramite credenziali
R2 in sola lettura (vedi Control Plane).
Procedura di ripristino
# Selettore interattivo (backup locali + S3)
flo backup restore <id>
# File esplicito; i ripristini cross-tenant rimuovono l'ownership
flo backup restore target --file /path/to/source_backup.sql.gz --no-owner
# Ripristina un file locale su un tenant remoto
flo --vps production backup restore <id> --file ~/backups/flo-<ts>.sql.gz --from-local --fresh --no-restart -y
Controlli di sicurezza integrati in restore:
- Guardia DR — un tenant con blocco DR (
tenant.dr) è rifiutato: rimuovere prima replicazione e binding degli agent. - Verifica checksum — il sidecar
.sha256è verificato quando presente;--require-checksumfallisce in modo chiuso sui backup legacy senza sidecar. - Snapshot pre-ripristino — il database corrente è scaricato in
backupDir/restore-<ts>.pre.sql.gzprima di sovrascrivere qualsiasi cosa. - Singola transazione — il ripristino gira dentro un'unica transazione
psql; un fallimento a metà flusso esegue rollback. - Conferma — prompt distruttivo salvo
-y. - Ripristino completo — l'archivio è decifrato e richiede il dump database corrispondente
accanto (
-full.tar.gzvicino a.dumpo.sql.gz); entrambi sono verificati via checksum.--from-localsupporta solo backup database.
--fresh azzera lo schema public del target e rimuove ownership/ACL sorgenti
(ripristino cross-tenant idempotente); implica --no-owner. --no-restart lascia
i container applicativi fermi dopo il tentativo.
Relazione con lo script legacy rclone
Backup Automatici documenta lo script legacy per singolo tenant
backup_db.sh che copia i dump su Google Drive via rclone su un cron ogni 6 ore.
Quel flusso è autonomo e non ha livelli GFS, cifratura, sidecar checksum né
retention da lifecycle. Il sistema GFS della CLI qui sopra lo sostituisce per i deploy
multi-tenant gestiti; conserva la pagina legacy solo per il setup standalone/self-hosted.