Operator di backup & ripristino per Kubernetes · Postgres 15–17 · PostGIS · MySQL 8.4
Non perdere nemmeno un secondo.
Tenax esegue il backup dei database Postgres e MySQL che fai girare in Kubernetes sul tuo bucket S3 e trasmette ogni modifica nel momento in cui avviene. Scegli un secondo nella finestra e riavrai esattamente quel database in una sandbox che puoi interrogare. Cifrato prima di lasciare il cluster, progettato crash-only perché non si blocchi mai.
Un binario, un chart. Il tuo bucket, le tue chiavi.
- Ripristina a
- 2026-10-10 14:02:37 UTC
- Backup di base
- 20261010T020712Z-4c1e
- Poi riapplica
- 114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --wait
Guardalo riavvolgere, o trascina tu stesso lungo la finestra. Le tacche color brace sono i backup completi notturni; la banda ciano è il flusso di modifiche tra un backup e l'altro. Un'illustrazione di una policy prod: completi ogni notte, finestra di 7 giorni.
- 9sper ripristinare un database Postgres da 644 MiB in una sandbox: download, decifratura, recovery ed esecuzione dei controlli
- 4.2×più piccolo nel bucket: un backup fisico da 683 MiB salvato in 161 MiB
- 72MiB/sthroughput di un backup completo logico su 4 vCPU, in streaming senza file temporanei
- 20msp99 della panoramica della console con 1.000 database e 100.000 backup
Misurato con make test-perf e make test-ui sull'ambiente di test k3d (4 vCPU, MinIO nel cluster). I numeri sono in docs/PERF.md nel repository.
01Come funzionacinque passi
Da una modifica in produzione a una sandbox che puoi interrogare.
Nessun agente dentro il tuo database, nessuno snapshot di volume. Tenax legge le modifiche via rete, le sigilla prima che lascino il pod e riporta indietro qualsiasi secondo quando lo chiedi.
- 01
Streaming
Backup completi pianificati, più ogni modifica nel momento in cui avviene: il receiver WAL di Tenax per Postgres, mysqlbinlog per MySQL.
WAL · binlog - 02
Cifratura
Ogni byte è compresso con zstd e cifrato con age verso le tue chiavi pubbliche, dentro il pod, in un unico passaggio in streaming senza file temporanei.
zstd → age - 03
Archiviazione
Upload multipart in parallelo su qualsiasi bucket compatibile S3, uno SHA-256 per ogni oggetto e il manifest scritto per ultimo.
*.zst.age · manifest.json - 04
Ripristino al secondo
Chiedi un orario o un ID di backup. Tenax sceglie il backup di base e riapplica le modifiche fino a quel preciso secondo.
--at 14:02:37 - 05
Apri una sandbox
Il risultato finisce in un database privato con un TTL e una console di query in sola lettura. I tuoi controlli vengono eseguiti prima che il ripristino conti come riuscito.
--ttl 6h
02Ripristinoquattro destinazioni
Ripristina ovunque, senza toccare la produzione.
Un ripristino è una risorsa Kubernetes come tutte le altre. Chiedi un orario o un ID di backup e scegli dove deve andare. Tenax pianifica il backup di base e le modifiche da riapplicare, poi esegue i tuoi controlli prima di dichiarare riuscito il ripristino.
Una copia usa e getta
Un database privato con un TTL, credenziali e una console di query in sola lettura. Si elimina da solo.
--to sandbox --ttl 6hUn nuovo database
Ripristinato e controllato in una sandbox di staging, poi copiato con un nuovo nome. Gli oggetti esistenti non vengono mai sovrascritti.
--to new --database app_copyTorna indietro per davvero
Esegue prima un backup di sicurezza e ti chiede di digitare il nome del target. Richiede il ruolo restorer-admin.
--to in-place --confirm appUn file di dump
Un dump logico decifrato e compresso dietro un link che scade dopo un'ora. Registrato nell'audit.
--to download03Guardaloun minuto
Un minuto, dall'avvio al ripristino.
Comandi veri e la console vera su un catalogo demo: elenca ogni database, guarda cosa è cambiato durante la notte, ripristinalo in una sandbox.
La console
Scopri cosa è cambiato tra due versioni.
Ogni backup porta con sé un inventario di tabelle, righe e dimensioni. Ogni segmento di modifiche porta un riepilogo di ciò che ha fatto. Così, prima di ripristinare qualcosa, puoi vedere quale versione aveva ancora le righe che hai perso.
- Confronti tra due backup qualsiasi. Modifiche allo schema, conteggi delle righe e dimensioni per tabella.
- Riepiloghi delle modifiche per segmento WAL o binlog. Insert, update, delete e DDL per tabella, così un TRUNCATE alle 22:48 si trova facilmente.
- Anteprime mascherate. Righe di esempio con le colonne che indichi mascherate, leggibili con una chiave di catalogo che non può decifrare i dati stessi.
La CLI
$ tenax targets TARGET ENGINE ORG/PROJECT/ENV LAST FULL PITR WINDOW STORED HEALTH analytics/events postgres acme/data/staging 2026-10-07 02:11:47Z - 19.1GiB ok auth/users postgres acme/platform/prod 2026-10-07 02:11:06Z 2026-09-23 02:11:06Z → 2026-10-07 18:50:52Z 1.5GiB ok billing/invoices mysql acme/billing/prod 2026-10-07 02:09:03Z 2026-09-23 02:09:03Z → 2026-10-07 18:50:52Z 6.7GiB ok billing/ledger postgres acme/billing/prod 2026-10-07 02:08:22Z 2026-09-23 02:08:22Z → 2026-10-07 18:50:52Z 14.3GiB ok maps/geo postgres acme/maps/prod 2026-10-07 02:10:25Z 2026-09-23 02:10:25Z → 2026-10-07 18:50:52Z 23.3GiB ok shop-dev/orders-db postgres acme/shop/dev 2026-10-07 02:12:28Z - 17.4MiB ok shop/catalog-db postgres acme/shop/prod 2026-10-07 02:07:41Z 2026-09-23 02:07:41Z → 2026-10-07 18:50:52Z 731.0MiB ok shop/orders-db postgres acme/shop/prod 2026-10-07 02:07:00Z 2026-09-23 02:07:00Z → 2026-10-07 18:50:52Z 9.6GiB ok web/wordpress mysql acme/marketing/prod 2026-10-07 02:09:44Z 2026-09-23 02:09:44Z → 2026-10-07 18:50:52Z 607.4MiB ok
$ tenax diff 20261006T020700Z-bd79 20261007T020700Z-6da5 from 20261006T020700Z-bd79 (2026-10-06 02:07:00Z) to 20261007T020700Z-6da5 (2026-10-07 02:07:00Z) 20261007 applied (was 20261006) column orders.gift_note added table audit_log +158,175 rows (+2%) table order_items +62,923 rows (+1%) table orders +15,655 rows (+1%) table coupons −3,120 rows (−100%) table customers +531 rows (+1%) 14 segments in between: 00000001000000A100000000 18:51:32–19:20:32 · 800 commits · audit_log +3348 / ~1116 / −83 · order_items +1327 / ~442 / −33 · orders +330 / ~110 / −8 · customers +11 / ~3 / −0 00000001000000A100000001 19:21:32–19:50:32 · 813 commits · audit_log +3352 / ~1116 / −83 · order_items +1328 / ~442 / −33 · orders +330 / ~110 / −8 · customers +13 / ~3 / −0 00000001000000A100000002 19:51:32–20:20:32 · 826 commits · audit_log +3356 / ~1116 / −83 · order_items +1329 / ~442 / −33 · orders +330 / ~110 / −8 · customers +15 / ~3 / −0
$ tenax restore shop/orders-db --at 20261006T020700Z-bd79 --ttl 6h --reason "coupons truncated" restore orders-db-sandbox-60414a15fc created (sandbox from 20261006T020700Z-bd79, 0 segments) restore shop/orders-db-sandbox-60414a15fc $ tenax sandbox list NAME ENGINE SOURCE OWNER PHASE EXPIRES IN-CLUSTER sbx-7f3a postgres shop/orders-db maria@acme.example Ready 2026-10-08 00:37:32Z tenax-sbx-sbx-7f3a.tenax-sandboxes.svc:5432 sbx-c210 mysql billing/invoices dev@acme.example Provisioning -
Output del server demo nel repository: go run ./hack/demo, poi punta la CLI o un browser verso di esso.
Console, CLI e API
Un'unica API JSON descritta da OpenAPI, con i ruoli da Cloudflare Access. Ogni scrittura e ogni query viene registrata nell'audit.
Nativo GitOps
Store, policy, target e ripristini sono CRD. Livelli di conservazione, alert e una dashboard Grafana sono inclusi nel chart.
Verificato, non sperato
I ripristini di verifica pianificati eseguono i tuoi controlli SQL su un ripristino reale e marcano il backup come verificato. Il loro tempo di ripristino viene esportato come metrica.
04Affidabilitàcrash-only
Non si blocca mai. Un kill -9 non lo scalfisce.
Di notte i backup falliscono in modi banali: un mount bloccato, un pod morto, un file scritto a metà. Tenax è costruito in modo che nessuno di questi possa incepparlo o spacciarsi per un backup.
- data/part-00001.zst.age
- data/part-00002.zst.age
- data/part-00003.zst.age
- manifest.json
- data/part-00001.zst.age
- data/part-00002.zst.age
- data/part-00003.zst.age
- manifest.json
Non si blocca mai
Nessun mount né block device; database solo via rete. Ogni flusso ha un watchdog sull'avanzamento e ogni tool gira nel proprio process group.
Crash-only
Termina qualsiasi pod in qualsiasi momento. Un backup esiste solo quando il suo manifest è stato scritto per ultimo, e la suite chaos termina Tenax a metà backup per dimostrarlo.
I dati si spostano nei Job, mai nel controller.
Il controller si limita a pianificare. Mover e streamer portano i byte attraverso un'unica pipeline in streaming dritti nel tuo bucket, e il restorer li riporta indietro.
Delicato con la produzione
Un solo utente di database con privilegi minimi che non scrive mai. Slot per host, limiti di banda e una protezione dal carico che rinvia un backup quando il server è occupato.
Streaming delle modifiche
Il receiver WAL di Tenax su uno slot di replica per Postgres e mysqlbinlog per MySQL. La finestra arriva fino al presente, anche su un database poco attivo.
Veloce per costruzione
Pipeline in streaming senza file temporanei, upload multipart in parallelo, zstd multi-thread e un catalogo in memoria dietro una console che risponde in millisecondi.
Così leggero che ti dimentichi che c'è.
Tenax è un unico binario Go. Il controller e l'API si limitano a pianificare e leggere; i byte si spostano in Job di breve durata che terminano a lavoro finito, così tra un backup e l'altro nel tuo cluster non resta niente di pesante.
Working set riportato dal kubelet nel cluster di test k3d (12 database) dopo le suite end-to-end e chaos; a riposo la CPU è di pochi millicore. Ogni database con point-in-time recovery esegue anche uno streamer, circa 70 MiB durante lo streaming.
05Sicurezzacifrato alla fonte
Il tuo bucket vede solo testo cifrato.
La cifratura avviene nel pod, prima dell'upload, verso chiavi che Tenax non possiede. L'accesso è diviso per ruoli, e ogni scrittura e ogni query lascia un record di audit.
$ head -c 200 data/part-00001.zst.age age-encryption.org/v1 -> X25519 Qm9yZWQ/IE5vdGhpbmcgdG8gc2VlIGhlcmUu… --- 7Hc1kS0zR2QeYc… ÌÞ#ø§ñ¹t¤¢Ý}ϰë։ÿ]Wœ3Ð÷…
Ogni oggetto di dati è un file age cifrato per i tuoi destinatari (X25519). Solo il manifest è JSON in chiaro, e non contiene segreti.
Cifrato prima di uscire
Ogni oggetto è cifrato lato client con age verso le tue chiavi. Il bucket non vede mai dati in chiaro, e la chiave di catalogo della console non può leggere i dati.
Una chiave di catalogo che non può leggere i dati
La console legge inventari e anteprime mascherate con un'identità di catalogo separata. Non può decifrare dump, backup di base, WAL o binlog.
Controllato da cima a fondo
Il manifest contiene uno SHA-256 per ogni oggetto salvato, controllato alla fine di ogni lettura, ed è a sua volta scritto per ultimo con un PUT condizionale.
I segreti restano nascosti
Le credenziali del database arrivano ai tool figli tramite file sigillati in memoria, mai tramite argv o l'ambiente. I log oscurano tutto ciò che sembra un segreto.
Ruoli e audit trail
Accesso tramite Cloudflare Access e quattro ruoli, da viewer a restorer-admin. Il ripristino sul posto richiede il ruolo più alto e una conferma digitata.
Pod blindati
Non-root, root filesystem in sola lettura, tutte le capability rimosse, seccomp RuntimeDefault. I pod sul percorso dei dati montano solo emptyDir.
06A confrontocon un cron job
Più di un dump notturno in un bucket.
| Quando devi… | script cron + pg_dump | Tenax |
|---|---|---|
| Tornare alle 09:41:27 | Il dump di stanotte, fino a 24 h perse | Qualsiasi secondo nella finestra |
| Dimostrare che un backup si ripristina | Lo scopri durante l'incidente | Ripristini di verifica pianificati con i tuoi controlli |
| Trovare la versione con le righe mancanti | Ripristinarne diverse e guardare | Confronti e riepiloghi delle modifiche nella console |
| Guardare senza rischi | Ripristinare sopra un database di staging condiviso | Una sandbox privata con un TTL |
| Tenere il bucket all'oscuro | Cifratura lato server, se configurata | Cifratura age lato client verso le tue chiavi |
| Sopravvivere a un nodo che muore a metà backup | Un file scritto a metà che sembra completo | Niente conta finché il manifest non è scritto per ultimo |
| Sapere che funziona ancora | Silenzio | Alert per backup in ritardo, catene interrotte e ritardo del flusso |
07Installazionecirca dieci minuti
Da zero a ripristinabile.
Installa il chart
CRD, il controller, l'API e la console, RBAC, alert e la dashboard.
helm install tenax charts/tenax -n tenax-system --create-namespace \ --set image.repository=registry.example.com/tenax/tenax --set image.tag=v0.1.0 \ --set 'targetNamespaces={team-a}'Collegalo a un bucket e alle tue chiavi
Qualsiasi store compatibile S3. Genera una chiave age con age-keygen e conservane una copia offline.
apiVersion: tenax.run/v1alpha1 kind: BackupStore metadata: { name: main } spec: s3: endpoint: https://s3.example.com region: us-east-1 bucket: db-backups credentialsSecret: { namespace: tenax-system, name: tenax-s3 } encryption: ageRecipients: [age1…] # solo la chiave pubblica; quella privata resta offline
Dichiara un target accanto al database
Crea l'utente di database tenax con l'SQL in docs/sql/, poi fai il commit del target in git.
apiVersion: tenax.run/v1alpha1 kind: BackupTarget metadata: { name: app, namespace: team-a } spec: engine: postgres endpoint: { host: postgres, port: 5432 } databases: [app] policy: prod # completi ogni notte, finestra point-in-time di 7 giorni credentials: secretRef: { name: tenax-postgres }
Ripristina qualcosa, di proposito
Il primo backup completo parte da solo. Poi prova la cosa di cui speri di non avere mai bisogno.
tenax targets tenax restore team-a/app --at 2026-10-07T09:41:27Z --wait tenax query <sandbox> -e "select count(*) from orders"
La guida completa, ogni campo dei CRD e la REST API sono nella documentazione.
08FAQrisposte dirette
Le prime domande degli ingegneri.
Quali database supporta Tenax?
Postgres 15, 16 e 17, PostGIS compreso, con backup completi fisici o logici e streaming del WAL. MySQL 8.4 con backup completi logici e streaming del binlog.
Quanto indietro posso ripristinare, e con quale precisione?
Qualsiasi backup completo conservato dai tuoi livelli di conservazione, e qualsiasi secondo dentro la finestra point-in-time impostata dalla tua policy. La policy prod di esempio mantiene una finestra di 7 giorni e 14 completi giornalieri, 8 settimanali, 12 mensili e 5 annuali.
Tenax o il mio provider di storage possono leggere i miei dati?
Il bucket no: ogni oggetto è cifrato con age per i tuoi destinatari prima dell'upload. La console legge gli inventari con una chiave di catalogo che non può decifrare i dati stessi. I ripristini decifrano con un'identità age che fornisci tu, quindi conservane una copia offline.
Cosa succede se un pod muore nel mezzo di un backup?
Niente che conti. Un backup esiste solo quando il suo manifest è scritto, dopo che ogni oggetto è stato caricato e ne è stato calcolato il checksum. Un'esecuzione lasciata a metà non ha manifest, quindi non viene mai proposta per il ripristino. La suite chaos termina Tenax a metà backup per dimostrarlo.
Tenax monta volumi o usa snapshot?
No. Raggiunge i database solo via rete, e i suoi pod non montano altro che emptyDir: niente hostPath, FUSE, iSCSI o block device, e nessun PVC per impostazione predefinita. Un driver di storage bloccato non può bloccare un backup.
Di cosa ha bisogno Tenax dal mio database?
Un utente tenax con privilegi minimi per database che non scrive mai, creato con l'SQL in docs/sql/. Senza i diritti di replica Tenax esegue comunque backup logici e ti dice cosa concedere per poter ripristinare qualsiasi secondo.
Tenax è open source?
Il codice è disponibile con The Republic Lex Aequa License 1.0. Chiunque può usarlo, modificarlo e ridistribuirlo gratuitamente, anche in produzione in aziende commerciali, per proteggere i propri dati. La vendita o l'hosting come servizio per terzi sono riservati.