Tenax
IT
GitHub

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.

Finestra point-in-time · team/app · policy prod finestra 7 giorni · catena integra

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.

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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.

SANDBOX

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 6h
NEW

Un 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_copy
IN PLACE

Torna 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 app
DOWNLOAD

Un file di dump

Un dump logico decifrato e compresso dietro un link che scade dopo un'ora. Registrato nell'audit.

--to download

03Guardaloun 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.

0:58 · con audio · 1,9 MB

La console

Panoramica della console di Tenax: nove database con ultimo backup completo, finestra point-in-time, ritardo, dimensione salvata e stato
Tutti i database a colpo d'occhio: ultimo backup completo, finestra di ripristino, ritardo del flusso, dimensione e stato.
Pagina di un target nella console di Tenax: la timeline ripristinabile, gli andamenti di righe e dimensioni e l'elenco delle versioni con i badge di verifica
Un database: la sua timeline ripristinabile, i suoi andamenti e ogni versione, con i ripristini verificati in evidenza.
Confronto tra due backup nella console di Tenax: una colonna aggiunta, una tabella svuotata, righe e dimensioni per tabella
Due versioni a confronto: cosa è stato applicato, quali colonne sono cambiate, righe e dimensioni per tabella.

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

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
La CLI
$ 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
La CLI
$ 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.

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.

54MBun unico binario statico: controller, API e console, mover, streamer, restorer e CLI
18MBdownload compresso, nessun runtime da installare, nessun sidecar
21MiBmemoria del controller a riposo, mentre sorveglia un cluster di database
19MiBmemoria di API e console a riposo, con il catalogo in memoria

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.

Cosa vede il tuo bucket
$ 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_dumpTenax
Tornare alle 09:41:27Il dump di stanotte, fino a 24 h perseQualsiasi secondo nella finestra
Dimostrare che un backup si ripristinaLo scopri durante l'incidenteRipristini di verifica pianificati con i tuoi controlli
Trovare la versione con le righe mancantiRipristinarne diverse e guardareConfronti e riepiloghi delle modifiche nella console
Guardare senza rischiRipristinare sopra un database di staging condivisoUna sandbox privata con un TTL
Tenere il bucket all'oscuroCifratura lato server, se configurataCifratura age lato client verso le tue chiavi
Sopravvivere a un nodo che muore a metà backupUn file scritto a metà che sembra completoNiente conta finché il manifest non è scritto per ultimo
Sapere che funziona ancoraSilenzioAlert per backup in ritardo, catene interrotte e ritardo del flusso

07Installazionecirca dieci minuti

Da zero a ripristinabile.

  1. 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}'
  2. 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
  3. 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 }
  4. 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.

Prova il ripristino prima di averne bisogno.