Passa al contenuto principale

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​

FormaContenuti
flo backup create <id>pg_dump --clean --if-exists compresso con gzip → flo-<timestamp>.sql.gz
flo backup create <id> --fullIl 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>.sh sull'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​

ComandoDescrizioneOpzioni 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 .sha256 e gli snapshot pre-ripristino.
  • R2: <bucket>/daily/, <bucket>/weekly/, <bucket>/monthly/, con gli oggetti .sha256 corrispondenti 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:

  1. Guardia DR — un tenant con blocco DR (tenant.dr) è rifiutato: rimuovere prima replicazione e binding degli agent.
  2. Verifica checksum — il sidecar .sha256 è verificato quando presente; --require-checksum fallisce in modo chiuso sui backup legacy senza sidecar.
  3. Snapshot pre-ripristino — il database corrente è scaricato in backupDir/restore-<ts>.pre.sql.gz prima di sovrascrivere qualsiasi cosa.
  4. Singola transazione — il ripristino gira dentro un'unica transazione psql; un fallimento a metà flusso esegue rollback.
  5. Conferma — prompt distruttivo salvo -y.
  6. Ripristino completo — l'archivio è decifrato e richiede il dump database corrispondente accanto (-full.tar.gz vicino a .dump o .sql.gz); entrambi sono verificati via checksum. --from-local supporta 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.