Backup- und Restore-Operator für Kubernetes · Postgres 15–17 · PostGIS · MySQL 8.4
Jede Sekunde, festgehalten.
Tenax sichert die Postgres- und MySQL-Datenbanken, die Sie in Kubernetes betreiben, in Ihren eigenen S3-Bucket und streamt jede Änderung, sobald sie passiert. Wählen Sie eine Sekunde im Fenster und Sie bekommen genau diese Datenbank in einer Sandbox zurück, die Sie abfragen können. Verschlüsselt, bevor die Daten den Cluster verlassen, und crash-only gebaut, damit nichts hängen bleibt.
Ein Binary, ein Chart. Ihr Bucket, Ihre Schlüssel.
- Wiederherstellen auf
- 2026-10-10 14:02:37 UTC
- Basis-Backup
- 20261010T020712Z-4c1e
- Danach einspielen
- 114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --wait
Sehen Sie zu, wie es zurückspult, oder ziehen Sie selbst über das Fenster. Die orangefarbenen Marken sind die nächtlichen Vollsicherungen, das cyanfarbene Band ist der Änderungsstrom dazwischen. Illustration einer prod-Richtlinie: nächtliche Vollsicherung, 7-Tage-Fenster.
- 9sum eine 644-MiB-Postgres-Datenbank in eine Sandbox zurückzuholen: laden, entschlüsseln, wiederherstellen, prüfen
- 4.2×kleiner im Bucket: ein physisches Backup von 683 MiB, gespeichert als 161 MiB
- 72MiB/sDurchsatz einer logischen Vollsicherung auf 4 vCPU, gestreamt ohne temporäre Dateien
- 20msp99 der Konsolen-Übersicht mit 1.000 Datenbanken und 100.000 Backups
Gemessen mit make test-perf und make test-ui in der k3d-Testumgebung (4 vCPU, MinIO im Cluster). Die Zahlen stehen in docs/PERF.md im Repository.
01So funktioniert esfünf Schritte
Von der Live-Änderung zur Sandbox, die Sie abfragen können.
Kein Agent in Ihrer Datenbank, keine Volume-Snapshots. Tenax liest Änderungen über das Netzwerk, versiegelt sie, bevor sie den Pod verlassen, und holt jede Sekunde zurück, wenn Sie danach fragen.
- 01
Streamen
Geplante Vollsicherungen und dazu jede Änderung, sobald sie passiert: Tenax' eigener WAL-Empfänger für Postgres, mysqlbinlog für MySQL.
WAL · binlog - 02
Verschlüsseln
Jedes Byte wird mit zstd komprimiert und mit age für Ihre öffentlichen Schlüssel verschlüsselt, im Pod, in einem einzigen Streaming-Durchlauf ohne temporäre Dateien.
zstd → age - 03
Speichern
Parallele Multipart-Uploads in jeden S3-kompatiblen Bucket, ein SHA-256 für jedes Objekt, und das Manifest kommt zuletzt.
*.zst.age · manifest.json - 04
Sekundengenau wiederherstellen
Fragen Sie nach einer Uhrzeit oder einer Backup-ID. Tenax wählt die Basissicherung und spielt die Änderungen bis genau zu dieser Sekunde ein.
--at 14:02:37 - 05
Sandbox öffnen
Das Ergebnis landet in einer privaten Datenbank mit TTL und einer schreibgeschützten Abfragekonsole. Ihre Prüfungen laufen, bevor die Wiederherstellung als erfolgreich gilt.
--ttl 6h
02Wiederherstellenvier Ziele
Überall wiederherstellen, ohne die Produktion anzufassen.
Eine Wiederherstellung ist eine Kubernetes-Ressource wie jede andere. Geben Sie eine Zeit oder eine Backup-ID an und wählen Sie das Ziel. Tenax plant das Basis-Backup und die einzuspielenden Änderungen und führt Ihre Prüfungen aus, bevor die Wiederherstellung als erfolgreich gilt.
Eine Wegwerf-Kopie
Eine private Datenbank mit TTL, Zugangsdaten und einer schreibgeschützten Abfragekonsole. Sie räumt sich selbst auf.
--to sandbox --ttl 6hEine neue Datenbank
In einer Staging-Sandbox wiederhergestellt und geprüft, dann unter neuem Namen kopiert. Bestehende Objekte werden nie überschrieben.
--to new --database app_copyWirklich zurückrollen
Erstellt zuerst ein Sicherheits-Backup und verlangt, dass Sie den Namen des Ziels eintippen. Erfordert die Rolle restorer-admin.
--to in-place --confirm appEine Dump-Datei
Ein entschlüsselter, komprimierter logischer Dump hinter einem Link, der nach einer Stunde abläuft. Auditiert.
--to download03Anseheneine Minute
Eine Minute, vom Start bis zur Wiederherstellung.
Echte Befehle und die echte Konsole auf einem Demo-Katalog: alle Datenbanken auflisten, sehen, was sich über Nacht geändert hat, und in eine Sandbox zurückholen.
Die Konsole
Wissen, was sich zwischen zwei Versionen geändert hat.
Jedes Backup trägt ein Inventar seiner Tabellen, Zeilen und Größen. Jedes Änderungssegment trägt eine Zusammenfassung dessen, was es getan hat. So sehen Sie vor jeder Wiederherstellung, welche Version die verlorenen Zeilen noch hatte.
- Diffs zwischen beliebigen zwei Backups. Schemaänderungen, Zeilenzahlen und Größe je Tabelle.
- Änderungsübersicht je WAL- oder Binlog-Segment. Inserts, Updates, Deletes und DDL je Tabelle, damit ein TRUNCATE um 22:48 leicht zu finden ist.
- Maskierte Vorschauen. Beispielzeilen mit maskierten Spalten Ihrer Wahl, lesbar mit einem Katalogschlüssel, der die Daten selbst nicht entschlüsseln kann.
Die 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 -
Ausgabe des Demo-Servers im Repository: go run ./hack/demo, dann die CLI oder einen Browser darauf richten.
Konsole, CLI und API
Eine JSON-API, beschrieben mit OpenAPI, mit Rollen aus Cloudflare Access. Jeder Schreibzugriff und jede Abfrage wird auditiert.
GitOps-nativ
Stores, Richtlinien, Ziele und Wiederherstellungen sind CRDs. Aufbewahrungsstufen, Alerts und ein Grafana-Dashboard sind im Chart enthalten.
Geprüft, nicht erhofft
Geplante Prüf-Wiederherstellungen führen Ihre SQL-Prüfungen auf einer echten Wiederherstellung aus und markieren das Backup als geprüft. Die Dauer wird als Metrik exportiert.
04Zuverlässigkeitcrash-only
Hängt nie. Steckt ein kill -9 weg.
Backups scheitern nachts auf langweilige Art: ein hängender Mount, ein toter Pod, eine halb geschriebene Datei. Tenax ist so gebaut, dass nichts davon es blockieren oder als Backup durchgehen kann.
- 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
Hängt nie
Keine Mounts, keine Block-Devices; Datenbanken nur über das Netzwerk. Jeder Strom hat einen Fortschritts-Watchdog, jedes Werkzeug läuft in seiner eigenen Prozessgruppe.
Crash-only
Beenden Sie jederzeit jeden Pod. Ein Backup existiert erst, wenn sein Manifest zuletzt geschrieben ist, und die Chaos-Suite beendet Tenax mitten im Backup, um das zu beweisen.
Daten bewegen sich in Jobs, nie im Controller.
Der Controller plant nur. Mover und Streamer tragen die Bytes durch eine einzige Streaming-Pipeline direkt in Ihren Bucket, und der Restorer holt sie zurück.
Schonend für die Produktion
Ein Datenbankbenutzer mit minimalen Rechten, der nie schreibt. Slots je Host, Bandbreitenlimits und ein Last-Wächter, der ein Backup verschiebt, wenn der Server ausgelastet ist.
Änderungsstrom
Tenaxs eigener WAL-Empfänger auf einem Replikations-Slot für Postgres und mysqlbinlog für MySQL. Das Fenster reicht bis zur Gegenwart, auch bei einer ruhigen Datenbank.
Schnell durch Konstruktion
Streaming-Pipelines ohne temporäre Dateien, parallele Multipart-Uploads, Multithread-zstd und ein Katalog im Speicher hinter einer Konsole, die in Millisekunden antwortet.
So leicht, dass man es vergisst.
Tenax ist ein einziges Go-Binary. Controller und API planen und lesen nur; Daten bewegen sich in kurzlebigen Jobs, die nach getaner Arbeit enden. Zwischen den Backups bleibt nichts Schweres im Cluster.
Working Set laut Kubelet im k3d-Testcluster (12 Datenbanken) nach End-to-End- und Chaos-Suite; die CPU im Ruhezustand liegt bei wenigen Millicores. Jede Datenbank mit Point-in-Time-Wiederherstellung betreibt zusätzlich einen Streamer, etwa 70 MiB während des Streamens.
05Sicherheitan der Quelle verschlüsselt
Ihr Bucket sieht nur Chiffretext.
Die Verschlüsselung passiert im Pod, vor dem Upload, mit Schlüsseln, die Tenax nicht besitzt. Der Zugriff ist nach Rollen getrennt, und jeder Schreibzugriff und jede Abfrage hinterlässt einen Audit-Eintrag.
$ head -c 200 data/part-00001.zst.age age-encryption.org/v1 -> X25519 Qm9yZWQ/IE5vdGhpbmcgdG8gc2VlIGhlcmUu… --- 7Hc1kS0zR2QeYc… ÌÞ#ø§ñ¹t¤¢Ý}ϰë։ÿ]Wœ3Ð÷…
Jedes Datenobjekt ist eine age-Datei, verschlüsselt für Ihre Empfänger (X25519). Nur das Manifest ist Klartext-JSON, und es enthält keine Geheimnisse.
Verschlüsselt, bevor es geht
Jedes Objekt wird clientseitig mit age für Ihre Schlüssel verschlüsselt. Der Bucket sieht nie Klartext, und der Katalogschlüssel der Konsole kann keine Daten lesen.
Ein Katalogschlüssel, der keine Daten liest
Die Konsole liest Inventare und maskierte Vorschauen mit einer eigenen Katalog-Identität. Sie kann weder Dumps noch Basissicherungen, WAL oder Binlogs entschlüsseln.
Durchgehend geprüft
Das Manifest enthält für jedes gespeicherte Objekt einen SHA-256, der am Ende jedes Lesevorgangs geprüft wird, und wird selbst zuletzt mit einem bedingten PUT geschrieben.
Geheimnisse bleiben verborgen
Zugangsdaten erreichen Kindprozesse über versiegelte Dateien im Speicher, nie über argv oder die Umgebung. Logs schwärzen alles, was nach einem Geheimnis aussieht.
Rollen und Audit-Trail
Anmeldung über Cloudflare Access und vier Rollen, von viewer bis restorer-admin. Eine In-Place-Wiederherstellung braucht die höchste Rolle und eine getippte Bestätigung.
Abgeriegelte Pods
Kein root, schreibgeschütztes Root-Dateisystem, alle Capabilities entzogen, Seccomp RuntimeDefault. Pods im Datenpfad mounten nur emptyDir.
06Im Vergleichzu einem Cronjob
Mehr als ein nächtlicher Dump im Bucket.
| Wenn Sie … | Cron + pg_dump-Skript | Tenax |
|---|---|---|
| auf 09:41:27 zurück müssen | Der Dump von gestern Nacht, bis zu 24 h verloren | Jede Sekunde im Fenster |
| beweisen müssen, dass ein Backup funktioniert | Erfahren Sie es während des Vorfalls | Geplante Prüf-Wiederherstellungen mit Ihren Prüfungen |
| die Version mit den fehlenden Zeilen finden müssen | Mehrere zurückholen und nachsehen | Diffs und Änderungsübersichten in der Konsole |
| ohne Risiko nachsehen wollen | Über ein geteiltes Staging zurückspielen | Eine private Sandbox mit TTL |
| den Bucket blind halten wollen | Serverseitige Verschlüsselung, falls eingerichtet | Clientseitige age-Verschlüsselung mit Ihren Schlüsseln |
| einen ausfallenden Knoten überstehen müssen | Eine halb geschriebene Datei, die vollständig aussieht | Nichts zählt, bevor das Manifest zuletzt geschrieben ist |
| wissen wollen, dass es noch läuft | Stille | Alerts für überfällige Backups, gebrochene Ketten und Verzögerung |
07Installierenetwa zehn Minuten
Von null auf wiederherstellbar.
Chart installieren
CRDs, Controller, API und Konsole, RBAC, Alerts und das 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}'Bucket und Schlüssel angeben
Jeder S3-kompatible Speicher. Erzeugen Sie einen age-Schlüssel mit age-keygen und bewahren Sie eine Offline-Kopie auf.
apiVersion: tenax.republic.global/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…] # nur der öffentliche Schlüssel; der private bleibt offline
Ein Ziel neben der Datenbank deklarieren
Legen Sie den Datenbankbenutzer tenax mit dem SQL aus docs/sql/ an und committen Sie das Ziel in Git.
apiVersion: tenax.republic.global/v1alpha1 kind: BackupTarget metadata: { name: app, namespace: team-a } spec: engine: postgres endpoint: { host: postgres, port: 5432 } databases: [app] policy: prod # nächtliche Vollsicherung, 7-Tage-Fenster credentials: secretRef: { name: tenax-postgres }
Etwas zurückholen, mit Absicht
Die erste Vollsicherung startet von selbst. Probieren Sie dann aus, was Sie hoffentlich nie brauchen.
tenax targets tenax restore team-a/app --at 2026-10-07T09:41:27Z --wait tenax query <sandbox> -e "select count(*) from orders"
Die vollständige Anleitung, jedes CRD-Feld und die REST-API stehen in der Dokumentation (auf Englisch).
08FAQklare Antworten
Was Ingenieure zuerst fragen.
Welche Datenbanken unterstützt Tenax?
Postgres 15, 16 und 17, einschließlich PostGIS, mit physischen oder logischen Vollsicherungen und WAL-Streaming. MySQL 8.4 mit logischen Vollsicherungen und Binlog-Streaming.
Wie weit zurück kann ich wiederherstellen, und wie genau?
Jede Vollsicherung, die Ihre Aufbewahrungsstufen behalten, und jede Sekunde im Point-in-Time-Fenster Ihrer Richtlinie. Die Beispielrichtlinie prod hält ein 7-Tage-Fenster sowie 14 tägliche, 8 wöchentliche, 12 monatliche und 5 jährliche Vollsicherungen.
Können Tenax oder mein Speicheranbieter meine Daten lesen?
Der Bucket nicht: Jedes Objekt wird vor dem Upload mit age für Ihre Empfänger verschlüsselt. Die Konsole liest Inventare mit einem Katalogschlüssel, der die Daten selbst nicht entschlüsseln kann. Wiederherstellungen entschlüsseln mit einer age-Identität, die Sie bereitstellen; bewahren Sie eine Offline-Kopie auf.
Was passiert, wenn ein Pod mitten in einem Backup stirbt?
Nichts, was zählt. Ein Backup existiert erst, wenn sein Manifest geschrieben ist, nachdem jedes Objekt hochgeladen und geprüft wurde. Ein halber Lauf hat kein Manifest und wird daher nie zur Wiederherstellung angeboten. Die Chaos-Suite beendet Tenax mitten im Backup, um das zu beweisen.
Mountet Tenax Volumes oder nutzt es Snapshots?
Nein. Es erreicht Datenbanken nur über das Netzwerk, und seine Pods mounten nichts außer emptyDir: kein hostPath, kein FUSE, kein iSCSI, keine Blockgeräte und standardmäßig keine PVCs. Ein hängender Speichertreiber kann kein Backup blockieren.
Was braucht Tenax von meiner Datenbank?
Einen tenax-Benutzer mit minimalen Rechten pro Datenbank, der nie schreibt, angelegt mit dem SQL in docs/sql/. Ohne Replikationsrechte erstellt Tenax trotzdem logische Backups und sagt Ihnen, was Sie für sekundengenaue Wiederherstellung freigeben müssen.
Ist Tenax Open Source?
Der Quellcode ist unter der The Republic Lex Aequa License 1.0 verfügbar. Jeder darf ihn kostenlos nutzen, ändern und weitergeben, auch in Produktion und in kommerziellen Unternehmen, um die eigenen Daten zu schützen. Verkauf oder Betrieb als Dienst für Dritte ist vorbehalten.