ADR-0012 - Backup strategy Postgres + Timescale
- Status: Accepted (2026-05-15)
- Deciders: Massimo Bagnoli (architect), Claude (sintesi Architect+DBA+Security)
- Implementation tasks: TASK-36 (pg_dump nightly), TASK-37 (restore test mensile), TASK-38 (WAL archiving PITR)
- Supersedes: nessuno
- Superseded by: nessuno
Context
Akira e' una piattaforma VoIP wholesale Class-4 con CDR continuativi. Il target di produzione e' 200 CPS, pari a circa 17M CDR/giorno. Il database TimescaleDB ospita l'hypertable cdr e le tabelle relazionali di supporto per companies, tariffs, routing, utenti, RBAC e configurazione operativa.
I task TASK-36, TASK-37 e TASK-38 definiscono rispettivamente backup nightly, restore drill mensile e WAL archiving. Questa ADR rende esplicite le motivazioni della scelta, i trade-off rispetto a strumenti dedicati e i target operativi.
Target iniziali:
- Staging: RPO 5 minuti, RTO 15 minuti.
- Produzione: RPO 1 minuto, RTO 30 minuti, da confermare in una ADR dedicata di fase produzione.
Vincoli operativi:
- Budget infra: sotto 10 EUR/mese per staging, sotto 50 EUR/mese per produzione.
- Network: server Hetzner in
fsn1; backup storage preferibilmente nella stessa region o nello stesso provider. - Skillset team: 1 developer principale piu' agenti AI, senza DBA dedicato.
- Compliance Fase 1: staging senza PII reale e senza requisito GDPR-specifico sui backup.
- Debbugabilita': la procedura deve essere comprensibile da un generalist e ripetibile in emergenza.
Decision
Adottiamo una strategia triple-layer basata su pg_dump custom, WAL archiving via SFTP e restore test periodico.
Layer 1 - Dump notturno con pg_dump
- Frequenza: 1 dump/notte alle 01:00 UTC.
- Comando:
pg_dump --format=custom --compress=9. - Formato: custom PostgreSQL, compresso e ripristinabile con
pg_restore. - Scope: schema e dati del database, incluse hypertable TimescaleDB e oggetti applicativi.
- Destinazione locale:
/var/backups/akira/. - Destinazione remota: Hetzner Storage Box BX11, directory
/backups/pg/, accesso SFTP. - Retention locale: 7 giorni.
- Retention remota: 30 giorni.
Il formato custom e' scelto perche' resta uno standard PostgreSQL, consente restore parallelo e rende il backup ispezionabile senza introdurre tool esterni.
Layer 2 - WAL archiving continuo per PITR
archive_mode = on.archive_command = /usr/local/bin/wal-archive.sh.archive_timeout = 300s, per forzare uno switch WAL almeno ogni 5 minuti anche con traffico basso.- Destinazione: stesso Storage Box, directory
/backups/wal/. - Retention WAL: 14 giorni.
- RPO effettivo staging: 5 minuti.
La retention WAL di 14 giorni copre il replay esteso dopo il restore di un dump fino a 7 giorni vecchio, lasciando margine operativo per ritardi o investigazioni.
Layer 3 - Restore test mensile
- Schedulazione: primo giorno del mese alle 04:00 UTC.
- Procedura: creare VM Hetzner ephemeral, installare PG16 + TimescaleDB, ripristinare l'ultimo dump, eseguire sanity check, appendere il drill log e distruggere la VM.
- Sanity check minimi: conteggio tabelle, ENUM, hypertable
cdr, funzioni e oggetti critici. - Costo stimato: trascurabile, circa millesimi di euro per drill.
Il restore test e' parte della strategia, non un controllo opzionale: senza verifica periodica il dump non dimostra recuperabilita' reale.
Consequences
Positive
- RPO staging 5 minuti raggiungibile con costo storage molto basso.
- Procedura debuggabile:
pg_dump,pg_restoree WAL PostgreSQL sono strumenti standard. - Nessun lock-in applicativo: dump custom PostgreSQL e WAL standard possono essere migrati verso S3, Backblaze, GCS o altra destinazione.
- Restore validato: il drill mensile intercetta dump corrotti, incompatibilita' di versione, oggetti TimescaleDB mancanti e regressioni procedurali.
- Schema evolution-friendly: il dump fotografa lo schema corrente senza richiedere replay manuale di migration storiche.
- Buon rapporto costo/beneficio: Storage Box BX11 e VM ephemeral bastano per Fase 1 staging.
Negative
- Dump full, non incrementale: ogni backup notturno contiene l'intero database. Con crescita storica dei CDR sara' necessario monitorare capienza e durata.
- Failure mode WAL: se lo Storage Box non e' raggiungibile, PostgreSQL accumula WAL in
pg_wal/; senza alert il disco puo' saturarsi. - Nessuna replica fisica in Fase 1: se
db-01e' indisponibile, il ripristino richiede una procedura di restore, non un failover immediato. - Lock condivisi durante dump:
pg_dumpnon blocca DML ordinario, ma puo' interferire con DDL e va schedulato fuori dalle finestre di maintenance. - Cifratura non obbligatoria in staging: accettabile per Fase 1 senza PII reale, ma da rivalutare prima della produzione.
Neutral
- Storage Box e' una scelta economica e semplice per Fase 1; non e' una dichiarazione definitiva per la produzione.
- La replica streaming futura resta complementare: migliora RTO e availability, ma non sostituisce backup e PITR.
Alternatives considered
A1 - Barman
Barman e' un backup manager PostgreSQL dedicato con gestione di WAL, retention e restore PITR.
Pro: soluzione matura, feature complete, standard in molte installazioni PostgreSQL.
Contro: richiede setup e gestione di un server Barman, curva di apprendimento maggiore e runbook specifici. Per staging con team piccolo e budget ridotto e' piu' complesso del necessario.
Verdict: rejected per Fase 1. Da rivalutare in produzione avanzata o quando esistera' un presidio DBA/ops piu' strutturato.
A2 - WAL-G con storage S3-compatible
WAL-G gestisce backup e WAL streaming verso object storage S3-compatible, con compressione e cifratura.
Pro: robusto, performante, adatto a storage object e scenari cloud-native.
Contro: introduce dipendenza da S3-compatible storage, credenziali e configurazione aggiuntiva. Non sfrutta direttamente Storage Box SFTP, che e' la scelta economica di Fase 1.
Verdict: rejected per Fase 1. Da riconsiderare se Storage Box diventa insufficiente o se la cifratura at-rest gestita dal tool diventa requisito.
A3 - pg_basebackup + WAL streaming
Approccio nativo PostgreSQL basato su backup fisico e WAL.
Pro: nessun tool esterno, supporta PITR nativo, allineato al modello di replica fisica.
Contro: backup binario legato alla major version e all'installazione PostgreSQL. Non produce un dump logico facilmente ispezionabile o ripristinabile in parallelo con pg_restore.
Verdict: rejected come soluzione primaria di Fase 1. Resta utile come base per una futura replica streaming db-02.
A4 - Snapshot Hetzner Cloud
Snapshot a livello block del disco o volume della VM.
Pro: rapido, semplice da avviare, utile per rollback infrastrutturale grossolano.
Contro: non garantisce consistenza applicativa se preso durante scritture, vincola al provider, non offre PITR minuto-per-minuto e non sostituisce un restore PostgreSQL verificato.
Verdict: rejected come backup primario. Possibile complemento settimanale in produzione per rollback emergenziale dell'intera VM.
Implementation references
- TASK-36:
infra/scripts/pg-backup-nightly.sh, cron 01:00 UTC e Ansible inroles/timescaledb/tasks/backup.yml. - TASK-37:
infra/scripts/dr-restore-test.sh, cron mensile, cloud-init VM ephemeral e sanity check. - TASK-38:
infra/scripts/wal-archive.sh, configurazionepostgresql.confconarchive_mode=onearchive_timeout=300. - TASK-43: alert
WALArchiveFailurese controlli supg_stat_archiver. - Storage Box prerequisito: ordinare BX11, creare sub-user
akira-staginge configurare le variabili vault dedicate.
Monitoring & success metrics
- Restore test mensile: target 100% success; ogni fail apre post-mortem e fix.
- Storage usage: alert se Storage Box supera 85% di utilizzo.
- WAL archive lag: alert se
pg_stat_archiver.last_archived_timeresta oltre 10 minuti nel passato. - WAL archive failures: alert se
pg_stat_archiver.failed_countaumenta. - Costo mensile staging: target sotto 10 EUR/mese.
- Durata restore drill: target sotto RTO staging di 15 minuti.
Open questions
- Prima della produzione decidere se cifrare i dump con GPG e chiave in vault (
vault_backup_encryption_key) o passare a un tool con encryption integrata. - Definire la procedura GDPR-compliant per cancellazione dati e retention backup quando entreranno dati reali multi-tenant.
- Confermare in una ADR successiva i target produzione RPO 1 minuto / RTO 30 minuti e l'eventuale introduzione di replica streaming.