Tenax
ID
GitHub

Operator pencadangan & pemulihan untuk Kubernetes · Postgres 15–17 · PostGIS · MySQL 8.4

Genggam erat setiap detik.

Tenax mencadangkan basis data Postgres dan MySQL yang Anda jalankan di Kubernetes ke bucket S3 milik Anda sendiri dan mengalirkan setiap perubahan saat terjadi. Pilih satu detik di dalam jendela dan dapatkan kembali basis data persis seperti saat itu di sandbox yang bisa Anda kueri. Dienkripsi sebelum meninggalkan klaster, dibangun crash-only sehingga tidak pernah macet.

Satu biner, satu chart. Bucket Anda, kunci Anda.

Jendela point-in-time · team/app · kebijakan prod jendela 7 hari · rantai utuh

Pulihkan ke
2026-10-10 14:02:37 UTC
Cadangan dasar
20261010T020712Z-4c1e
Lalu putar ulang
114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --wait

Saksikan ia memutar mundur, atau geser sendiri di sepanjang jendela. Tanda bara adalah cadangan penuh setiap malam; pita sian adalah aliran perubahan di antaranya. Ilustrasi kebijakan prod: cadangan penuh setiap malam, jendela 7 hari.

  • 9suntuk memulihkan basis data Postgres 644 MiB ke sandbox: mengambil, mendekripsi, memulihkan, dan menjalankan pemeriksaan
  • 4.2×lebih kecil di bucket: cadangan fisik 683 MiB disimpan sebagai 161 MiB
  • 72MiB/sthroughput cadangan penuh logis pada 4 vCPU, dialirkan tanpa berkas sementara
  • 20msp99 untuk ikhtisar konsol dengan 1.000 basis data dan 100.000 cadangan

Diukur dengan make test-perf dan make test-ui pada harness uji k3d (4 vCPU, MinIO di dalam klaster). Angka-angkanya ada di docs/PERF.md repositori.

01Cara kerjalima langkah

Dari perubahan langsung ke sandbox yang bisa Anda kueri.

Tanpa agen di dalam basis data Anda, tanpa snapshot volume. Tenax membaca perubahan melalui jaringan, menyegelnya sebelum meninggalkan pod, dan mengembalikan detik mana pun saat Anda memintanya.

  1. 01

    Alirkan

    Cadangan penuh terjadwal, ditambah setiap perubahan saat terjadi: penerima WAL milik Tenax sendiri untuk Postgres, mysqlbinlog untuk MySQL.

    WAL · binlog
  2. 02

    Enkripsi

    Setiap byte dikompresi dengan zstd dan dienkripsi dengan age ke kunci publik Anda, di dalam pod, dalam satu lintasan streaming tanpa berkas sementara.

    zstd → age
  3. 03

    Simpan

    Unggahan multipart paralel ke bucket apa pun yang kompatibel dengan S3, SHA-256 untuk setiap objek, dan manifest ditulis paling akhir.

    *.zst.age · manifest.json
  4. 04

    Pulihkan sampai ke detiknya

    Minta waktu atau ID cadangan. Tenax memilih cadangan dasar dan memutar ulang perubahan sampai detik yang tepat itu.

    --at 14:02:37
  5. 05

    Buka sandbox

    Hasilnya mendarat di basis data pribadi dengan TTL dan konsol kueri baca-saja. Pemeriksaan Anda berjalan sebelum pemulihan dianggap berhasil.

    --ttl 6h

02Pemulihanempat tujuan

Pulihkan ke mana saja, tanpa menyentuh produksi.

Pemulihan adalah resource Kubernetes seperti yang lain. Minta waktu atau ID cadangan, lalu pilih tujuannya. Tenax merencanakan cadangan dasar dan perubahan yang akan diputar ulang, lalu menjalankan pemeriksaan Anda sebelum menyatakan pemulihan berhasil.

SANDBOX

Salinan sekali pakai

Basis data pribadi dengan TTL, kredensial, dan konsol kueri baca-saja. Ia membersihkan dirinya sendiri.

--to sandbox --ttl 6h
NEW

Basis data baru

Dipulihkan dan diperiksa di sandbox staging, lalu disalin ke nama baru. Objek yang sudah ada tidak pernah ditimpa.

--to new --database app_copy
IN PLACE

Kembalikan sungguhan

Membuat cadangan pengaman terlebih dahulu dan meminta Anda mengetik nama target. Memerlukan peran restorer-admin.

--to in-place --confirm app
DOWNLOAD

Berkas dump

Dump logis terdekripsi dan terkompresi di balik tautan yang kedaluwarsa dalam satu jam. Diaudit.

--to download

03Lihatsatu menit

Satu menit, dari awal sampai pemulihan.

Perintah sungguhan dan konsol sungguhan pada katalog demo: tampilkan semua basis data, lihat apa yang berubah semalam, pulihkan ke sandbox.

0:58 · dengan suara · 1,9 MB

Konsol

Ikhtisar konsol Tenax: sembilan basis data dengan cadangan penuh terakhir, jendela point-in-time, lag, ukuran tersimpan, dan kesehatannya
Semua basis data dalam sekilas: cadangan penuh terakhir, jendela pemulihan, lag aliran, ukuran, dan kesehatan.
Halaman target di konsol Tenax: linimasa yang dapat dipulihkan, tren baris dan ukuran, serta daftar versi dengan lencana terverifikasi
Satu basis data: linimasa yang dapat dipulihkan, tren, dan setiap versinya, dengan pemulihan terverifikasi ditandai.
Diff konsol Tenax antara dua cadangan: satu kolom ditambahkan, satu tabel dikosongkan, baris dan ukuran per tabel
Dua versi berdampingan: apa yang diterapkan, kolom mana yang berubah, baris dan ukuran per tabel.

Ketahui apa yang berubah di antara dua versi.

Setiap cadangan membawa inventaris tabel, baris, dan ukurannya. Setiap segmen perubahan membawa ringkasan apa yang dilakukannya. Jadi sebelum memulihkan apa pun, Anda bisa melihat versi mana yang masih memiliki baris yang hilang.

  • Diff antara dua cadangan mana pun. Perubahan skema, jumlah baris, dan ukuran per tabel.
  • Ringkasan perubahan per segmen WAL atau binlog. Insert, update, delete, dan DDL per tabel, sehingga TRUNCATE pada pukul 22:48 mudah ditemukan.
  • Pratinjau tersamar. Contoh baris dengan kolom yang Anda tentukan disamarkan, dapat dibaca dengan kunci katalog yang tidak bisa mendekripsi data itu sendiri.

CLI

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
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
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  -

Keluaran dari server demo di repositori: go run ./hack/demo, lalu arahkan CLI atau browser ke sana.

Konsol, CLI, dan API

Satu API JSON yang dijelaskan dengan OpenAPI, dengan peran dari Cloudflare Access. Setiap penulisan dan setiap kueri diaudit.

Asli GitOps

Store, kebijakan, target, dan pemulihan adalah CRD. Tingkat retensi, peringatan, dan dasbor Grafana disertakan dalam chart.

Terverifikasi, bukan sekadar harapan

Pemulihan verifikasi terjadwal menjalankan pemeriksaan SQL Anda pada pemulihan sungguhan dan menandai cadangan sebagai terverifikasi. Waktu pemulihannya diekspor sebagai metrik.

04Keandalancrash-only

Tidak pernah macet. Santai menghadapi kill -9.

Pencadangan gagal di malam hari dengan cara yang membosankan: mount yang macet, pod yang mati, berkas yang setengah tertulis. Tenax dibangun agar tak satu pun dari itu bisa membuatnya tersangkut atau dianggap sebagai cadangan.

Tidak pernah macet

Tanpa mount atau block device; basis data hanya melalui jaringan. Setiap aliran punya watchdog kemajuan, dan setiap alat berjalan di process group-nya sendiri.

Crash-only

Matikan pod mana pun kapan saja. Cadangan baru ada setelah manifest-nya ditulis paling akhir, dan rangkaian uji chaos mematikan Tenax di tengah pencadangan untuk membuktikannya.

Data berpindah di Job, tidak pernah di controller.

Controller hanya menjadwalkan. Mover dan streamer membawa byte melalui satu pipeline streaming langsung ke bucket Anda, dan restorer mengembalikannya.

Ramah terhadap produksi

Satu pengguna basis data dengan hak minimum yang tidak pernah menulis. Slot per host, batas bandwidth, dan penjaga beban yang menunda pencadangan saat server sibuk.

Streaming perubahan

Penerima WAL milik Tenax sendiri pada replication slot untuk Postgres dan mysqlbinlog untuk MySQL. Jendelanya menjangkau saat ini, bahkan pada basis data yang sepi.

Cepat sejak rancangannya

Pipeline streaming tanpa berkas sementara, unggahan multipart paralel, zstd multi-thread, dan katalog di memori di balik konsol yang menjawab dalam hitungan milidetik.

Cukup kecil hingga Anda lupa ia ada.

Tenax adalah satu biner Go. Controller dan API hanya menjadwalkan dan membaca; byte berpindah di Job berumur pendek yang keluar setelah selesai, sehingga tidak ada beban berat di klaster Anda di antara pencadangan.

54MBsatu biner statis: controller, API dan konsol, mover, streamer, restorer, dan CLI
18MBunduhan terkompresi, tanpa runtime untuk dipasang, tanpa sidecar
21MiBmemori controller saat diam, mengawasi klaster berisi banyak basis data
19MiBmemori API dan konsol saat diam, dengan katalog di memori

Working set yang dilaporkan kubelet di klaster uji k3d (12 basis data) setelah rangkaian uji end-to-end dan chaos; CPU saat diam hanya beberapa milicore. Setiap basis data dengan pemulihan point-in-time juga menjalankan streamer, sekitar 70 MiB selama mengalirkan.

05Keamananterenkripsi di sumbernya

Bucket Anda hanya pernah melihat ciphertext.

Enkripsi terjadi di dalam pod, sebelum unggahan, ke kunci yang tidak dipegang Tenax. Akses dibagi menurut peran, dan setiap penulisan serta setiap kueri meninggalkan catatan audit.

Yang dilihat bucket Anda
$ head -c 200 data/part-00001.zst.age
age-encryption.org/v1
-> X25519 Qm9yZWQ/IE5vdGhpbmcgdG8gc2VlIGhlcmUu…
--- 7Hc1kS0zR2QeYc…
ÌÞ#ø§ñ¹t¤¢Ý}ϰë։ÿ]Wœ3Ð÷…

Setiap objek data adalah berkas age yang dienkripsi ke penerima Anda (X25519). Hanya manifest yang berupa JSON biasa, dan isinya tidak memuat rahasia.

  • Dienkripsi sebelum keluar

    Setiap objek dienkripsi di sisi klien dengan age ke kunci Anda. Bucket tidak pernah melihat plaintext, dan kunci katalog konsol tidak dapat membaca data.

  • Kunci katalog yang tidak dapat membaca data

    Konsol membaca inventaris dan pratinjau tersamar dengan identitas katalog terpisah. Identitas itu tidak dapat mendekripsi dump, cadangan dasar, WAL, maupun binlog.

  • Diperiksa dari ujung ke ujung

    Manifest memuat SHA-256 untuk setiap objek tersimpan, diperiksa di akhir setiap pembacaan, dan manifest sendiri ditulis paling akhir dengan PUT bersyarat.

  • Rahasia tetap tersembunyi

    Kredensial basis data sampai ke alat turunan melalui berkas tersegel di memori, tidak pernah lewat argv atau environment. Log menyamarkan apa pun yang tampak seperti rahasia.

  • Peran dan jejak audit

    Masuk melalui Cloudflare Access dan empat peran, dari viewer hingga restorer-admin. Pemulihan di tempat memerlukan peran tertinggi dan konfirmasi yang diketik.

  • Pod yang dikunci rapat

    Non-root, filesystem root baca-saja, semua capability dilepas, seccomp RuntimeDefault. Pod jalur data hanya me-mount emptyDir.

06Dibandingkandengan cron job

Lebih dari sekadar dump malam hari di bucket.

Saat Anda perlu…skrip cron + pg_dumpTenax
Kembali ke 09:41:27Dump semalam, hingga 24 jam hilangDetik mana pun di dalam jendela
Membuktikan cadangan bisa dipulihkanBaru ketahuan saat insidenPemulihan verifikasi terjadwal dengan pemeriksaan Anda
Menemukan versi dengan baris yang hilangPulihkan beberapa dan periksaDiff dan ringkasan perubahan di konsol
Melihat tanpa risikoPulihkan menimpa basis data staging bersamaSandbox pribadi dengan TTL
Menjaga bucket tetap butaEnkripsi sisi server, jika dikonfigurasiEnkripsi age sisi klien ke kunci Anda
Bertahan saat node mati di tengah pencadanganBerkas setengah jadi yang tampak lengkapTidak ada yang dihitung sampai manifest ditulis paling akhir
Tahu bahwa semuanya masih berjalanSunyiPeringatan untuk cadangan terlambat, rantai terputus, dan lag

07Instalasisekitar sepuluh menit

Dari nol sampai bisa dipulihkan.

  1. Pasang chart

    CRD, controller, API dan konsol, RBAC, peringatan, dan dasbor.

    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. Arahkan ke bucket dan kunci Anda

    Store apa pun yang kompatibel dengan S3. Buat kunci age dengan age-keygen dan simpan salinan 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…]   # hanya kunci publik; kunci privat tetap offline
  3. Deklarasikan target di samping basis data

    Buat pengguna basis data tenax dengan SQL di docs/sql/, lalu commit target ke 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             # cadangan penuh setiap malam, jendela point-in-time 7 hari
      credentials:
        secretRef: { name: tenax-postgres }
  4. Pulihkan sesuatu, dengan sengaja

    Cadangan penuh pertama dimulai dengan sendirinya. Lalu coba hal yang Anda harap tidak pernah dibutuhkan.

    tenax targets
    tenax restore team-a/app --at 2026-10-07T09:41:27Z --wait
    tenax query <sandbox> -e "select count(*) from orders"

Panduan lengkap, setiap field CRD, dan REST API ada di dokumentasi.

08FAQjawaban lugas

Yang pertama kali ditanyakan engineer.

Basis data apa saja yang didukung Tenax?

Postgres 15, 16, dan 17, termasuk PostGIS, dengan cadangan penuh fisik atau logis dan streaming WAL. MySQL 8.4 dengan cadangan penuh logis dan streaming binlog.

Seberapa jauh ke belakang saya bisa memulihkan, dan seberapa presisi?

Cadangan penuh mana pun yang disimpan tingkat retensi Anda, dan detik mana pun di dalam jendela point-in-time yang ditetapkan kebijakan Anda. Contoh kebijakan prod menyimpan jendela 7 hari serta 14 cadangan penuh harian, 8 mingguan, 12 bulanan, dan 5 tahunan.

Bisakah Tenax atau penyedia penyimpanan saya membaca data saya?

Bucket tidak bisa: setiap objek dienkripsi dengan age ke penerima Anda sebelum diunggah. Konsol membaca inventaris dengan kunci katalog yang tidak dapat mendekripsi data itu sendiri. Pemulihan mendekripsi dengan identitas age yang Anda berikan, jadi simpan salinan offline-nya.

Apa yang terjadi jika pod mati di tengah pencadangan?

Tidak ada yang berarti. Cadangan baru ada setelah manifest-nya ditulis, setelah setiap objek diunggah dan di-checksum. Proses yang setengah jalan tidak punya manifest, jadi tidak pernah ditawarkan untuk pemulihan. Rangkaian uji chaos mematikan Tenax di tengah pencadangan untuk membuktikannya.

Apakah Tenax me-mount volume atau memakai snapshot?

Tidak. Tenax menjangkau basis data hanya melalui jaringan, dan pod-nya tidak me-mount apa pun selain emptyDir: tanpa hostPath, FUSE, iSCSI, atau block device, dan tanpa PVC secara bawaan. Driver penyimpanan yang macet tidak dapat membuat pencadangan tersangkut.

Apa yang dibutuhkan Tenax dari basis data saya?

Satu pengguna tenax dengan hak minimum per basis data yang tidak pernah menulis, dibuat dengan SQL di docs/sql/. Tanpa hak replikasi, Tenax tetap membuat cadangan logis dan memberi tahu Anda apa yang perlu diberikan agar bisa memulihkan ke detik mana pun.

Apakah Tenax open source?

Kode sumbernya tersedia di bawah The Republic Lex Aequa License 1.0. Siapa pun boleh memakai, memodifikasi, dan mendistribusikannya ulang secara gratis, termasuk di produksi pada perusahaan komersial, untuk melindungi data mereka sendiri. Menjual atau menghostingnya sebagai layanan untuk pihak ketiga tidak diizinkan.

Uji pemulihan Anda sebelum Anda membutuhkannya.