Tenax
ES
GitHub

Operador de respaldo y restauración para Kubernetes · Postgres 15–17 · PostGIS · MySQL 8.4

Aférrate a cada segundo.

Tenax respalda en tu propio bucket S3 las bases de datos Postgres y MySQL que ejecutas en Kubernetes y transmite cada cambio en cuanto ocurre. Elige un segundo dentro de la ventana y recupera exactamente esa base de datos en un sandbox que puedes consultar. Cifrado antes de salir del clúster y diseñado para sobrevivir a cualquier caída, así que nunca se cuelga.

Un binario, un chart. Tu bucket, tus claves.

Ventana de restauración puntual · team/app · política prod ventana 7 días · cadena intacta

Restaurar a
2026-10-10 14:02:37 UTC
Respaldo base
20261010T020712Z-4c1e
Luego reproducir
114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --wait

Mira cómo rebobina o arrastra tú mismo por la ventana. Las marcas naranjas son los respaldos completos de cada noche; la banda cian es el flujo de cambios entre ellos. Ilustración de una política prod: completo cada noche, ventana de 7 días.

  • 9spara restaurar una base Postgres de 644 MiB en un sandbox: descargar, descifrar, recuperar y ejecutar verificaciones
  • 4.2×más pequeño en el bucket: un respaldo físico de 683 MiB guardado como 161 MiB
  • 72MiB/sde rendimiento en un respaldo lógico completo con 4 vCPU, en streaming y sin archivos temporales
  • 20msp99 de la vista general de la consola con 1.000 bases de datos y 100.000 respaldos

Medido con make test-perf y make test-ui en el entorno de pruebas k3d (4 vCPU, MinIO en el clúster). Los números están en docs/PERF.md del repositorio.

01Cómo funcionacinco pasos

De un cambio en vivo a un sandbox que puedes consultar.

Sin agentes dentro de tu base de datos ni snapshots de volúmenes. Tenax lee los cambios por la red, los sella antes de que salgan del pod y te devuelve cualquier segundo cuando lo pidas.

  1. 01

    Transmitir

    Respaldos completos programados y, además, cada cambio en cuanto ocurre: el receptor de WAL propio de Tenax para Postgres y mysqlbinlog para MySQL.

    WAL · binlog
  2. 02

    Cifrar

    Cada byte se comprime con zstd y se cifra con age para tus claves públicas, dentro del pod, en una sola pasada en streaming y sin archivos temporales.

    zstd → age
  3. 03

    Guardar

    Subidas multiparte en paralelo a cualquier bucket compatible con S3, un SHA-256 por objeto y el manifiesto escrito al final.

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

    Restaurar al segundo

    Pide una hora o un ID de respaldo. Tenax elige el respaldo base y reaplica los cambios hasta ese segundo exacto.

    --at 14:02:37
  5. 05

    Abrir un sandbox

    El resultado llega a una base de datos privada con TTL y una consola de consultas de solo lectura. Tus comprobaciones se ejecutan antes de dar la restauración por buena.

    --ttl 6h

02Restaurarcuatro destinos

Restaura donde quieras, sin tocar producción.

Una restauración es un recurso de Kubernetes como cualquier otro. Pide una hora o un ID de respaldo y elige el destino. Tenax planifica el respaldo base y los cambios a reproducir, y ejecuta tus verificaciones antes de dar la restauración por buena.

SANDBOX

Una copia desechable

Una base privada con TTL, credenciales y una consola de consultas de solo lectura. Se limpia sola.

--to sandbox --ttl 6h
NEW

Una base de datos nueva

Restaurada y verificada en un sandbox intermedio, luego copiada con un nombre nuevo. Nunca sobrescribe objetos existentes.

--to new --database app_copy
IN PLACE

Volver atrás de verdad

Primero hace un respaldo de seguridad y te pide escribir el nombre del destino. Requiere el rol restorer-admin.

--to in-place --confirm app
DOWNLOAD

Un archivo de volcado

Un volcado lógico descifrado y comprimido tras un enlace que caduca en una hora. Auditado.

--to download

03Verloun minuto

Un minuto, de cero a restaurar.

Comandos reales y la consola real sobre un catálogo de demostración: lista cada base de datos, mira qué cambió durante la noche y restáurala en un sandbox.

0:58 · con sonido · 1,9 MB

La consola

Vista general de la consola de Tenax: nueve bases de datos con su último respaldo completo, ventana de restauración, retraso, tamaño y estado
Todas las bases de un vistazo: último respaldo completo, ventana de restauración, retraso del flujo, tamaño y estado.
Página de una base de datos en la consola de Tenax: la línea de tiempo restaurable, tendencias de filas y tamaño, y la lista de versiones con insignias de verificación
Una base de datos: su línea de tiempo restaurable, sus tendencias y cada versión, con las restauraciones verificadas marcadas.
Diff entre dos respaldos en la consola de Tenax: una columna añadida, una tabla vaciada, filas y tamaño por tabla
Dos versiones lado a lado: qué migración se aplicó, qué columnas cambiaron, filas y tamaño por tabla.

Sabe qué cambió entre dos versiones.

Cada respaldo lleva un inventario de sus tablas, filas y tamaños. Cada segmento de cambios lleva un resumen de lo que hizo. Así, antes de restaurar nada, ves qué versión aún tenía las filas que perdiste.

  • Diffs entre dos respaldos cualesquiera. Cambios de esquema, número de filas y tamaño por tabla.
  • Resumen de cambios por segmento WAL o binlog. Inserciones, actualizaciones, borrados y DDL por tabla, para encontrar fácilmente un TRUNCATE a las 22:48.
  • Vistas previas enmascaradas. Filas de muestra con las columnas que indiques enmascaradas, legibles con una clave de catálogo que no puede descifrar los datos.

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  -

Salida del servidor de demostración del repositorio: go run ./hack/demo y apunta la CLI o un navegador a él.

Consola, CLI y API

Una API JSON descrita con OpenAPI, con roles de Cloudflare Access. Cada escritura y cada consulta se auditan.

Nativo de GitOps

Stores, políticas, targets y restauraciones son CRDs. Niveles de retención, alertas y un dashboard de Grafana vienen con el chart.

Verificado, no supuesto

Restauraciones de verificación programadas ejecutan tus consultas SQL contra una restauración real y marcan el respaldo como verificado. Su tiempo de restauración se exporta como métrica.

04Fiabilidada prueba de caídas

Nunca se cuelga. Ni un kill -9 lo inmuta.

Los respaldos fallan de noche de formas aburridas: un montaje atascado, un pod muerto, un archivo a medio escribir. Tenax está hecho para que ninguna de ellas lo bloquee ni pase por un respaldo.

Nunca se cuelga

Sin montajes ni dispositivos de bloque; bases de datos solo por la red. Cada flujo tiene un watchdog de progreso y cada herramienta corre en su propio grupo de procesos.

Crash-only

Mata cualquier pod en cualquier momento. Un respaldo existe solo cuando su manifiesto se escribe al final, y la suite de caos mata a Tenax a mitad de un respaldo para demostrarlo.

Los datos se mueven en Jobs, nunca en el controlador.

El controlador solo planifica. Movers y streamers llevan los bytes por un único pipeline en streaming directo a tu bucket, y el restorer los trae de vuelta.

Cuidadoso con producción

Un único usuario de base de datos con privilegios mínimos que nunca escribe. Turnos por host, límites de ancho de banda y una protección de carga que pospone el respaldo si el servidor está ocupado.

Streaming de cambios

El receptor WAL propio de Tenax sobre un slot de replicación para Postgres y mysqlbinlog para MySQL. La ventana llega al presente, incluso con una base inactiva.

Rápido por diseño

Pipelines en streaming sin archivos temporales, subidas multiparte en paralelo, zstd multihilo y un catálogo en memoria detrás de una consola que responde en milisegundos.

Tan ligero que olvidarás que está ahí.

Tenax es un único binario de Go. El controlador y la API solo planifican y leen; los datos se mueven en Jobs de vida corta que terminan al acabar, así que entre respaldos no queda nada pesado en tu clúster.

54MBun binario estático: controlador, API y consola, movers, streamers, restorer y CLI
18MBde descarga comprimida, sin runtime que instalar ni sidecars
21MiBde memoria del controlador en reposo, vigilando un clúster de bases de datos
19MiBde memoria de la API y la consola en reposo, con el catálogo en memoria

Working set que informa el kubelet en el clúster de pruebas k3d (12 bases de datos) tras las suites end-to-end y de caos; la CPU en reposo es de unos pocos milicores. Cada base con restauración puntual ejecuta además un streamer, unos 70 MiB mientras transmite.

05Seguridadcifrado en origen

Tu bucket solo ve datos cifrados.

El cifrado ocurre en el pod, antes de subir nada, con claves que Tenax no guarda. El acceso se reparte por roles y cada escritura y cada consulta deja un registro de auditoría.

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

Cada objeto de datos es un archivo age cifrado para tus destinatarios (X25519). Solo el manifiesto es JSON en claro, y no contiene secretos.

  • Cifrado antes de salir

    Cada objeto se cifra en el cliente con age para tus claves. El bucket nunca ve texto plano, y la clave de catálogo de la consola no puede leer datos.

  • Una clave de catálogo que no lee datos

    La consola lee inventarios y vistas previas enmascaradas con una identidad de catálogo aparte. No puede descifrar volcados, respaldos base, WAL ni binlogs.

  • Verificado de punta a punta

    El manifiesto guarda un SHA-256 por cada objeto almacenado, comprobado al final de cada lectura, y se escribe el último con un PUT condicional.

  • Los secretos, fuera de la vista

    Las credenciales llegan a las herramientas hijas mediante archivos sellados en memoria, nunca por argv ni por el entorno. Los logs ocultan todo lo que parezca secreto.

  • Roles y auditoría

    Inicio de sesión con Cloudflare Access y cuatro roles, de viewer a restorer-admin. Restaurar en el sitio exige el rol más alto y escribir una confirmación.

  • Pods blindados

    Sin root, sistema de archivos raíz de solo lectura, todas las capabilities eliminadas y seccomp RuntimeDefault. Los pods de datos solo montan emptyDir.

06Comparadocon un cron

Más que un volcado nocturno en un bucket.

Cuando necesitas…script cron + pg_dumpTenax
Volver a las 09:41:27El volcado de anoche, hasta 24 h perdidasCualquier segundo de la ventana
Demostrar que un respaldo se restauraDescubrirlo durante el incidenteRestauraciones de verificación programadas con tus comprobaciones
Encontrar la versión con las filas perdidasRestaurar varias y mirarDiffs y resúmenes de cambios en la consola
Mirar sin riesgoRestaurar sobre un staging compartidoUn sandbox privado con TTL
Que el bucket no vea nadaCifrado en el servidor, si se configuróCifrado age en el cliente con tus claves
Sobrevivir a un nodo que muere a mitadUn archivo a medias que parece completoNada cuenta hasta que el manifiesto se escribe al final
Saber que sigue funcionandoSilencioAlertas por respaldos atrasados, cadenas rotas y retraso

07Instalarunos diez minutos

De cero a restaurable.

  1. Instala el chart

    CRDs, el controlador, la API y la consola, RBAC, alertas y el 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. Apúntalo a un bucket y a tus claves

    Cualquier almacenamiento compatible con S3. Genera una clave age con age-keygen y guarda una copia offline.

    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…]   # solo la clave pública; la privada queda offline
  3. Declara un target junto a la base de datos

    Crea el usuario tenax con el SQL de docs/sql/ y sube el target a 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             # completos cada noche, ventana de 7 días
      credentials:
        secretRef: { name: tenax-postgres }
  4. Restaura algo, a propósito

    El primer respaldo completo empieza solo. Luego prueba eso que esperas no necesitar nunca.

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

La guía completa, cada campo de los CRD y la API REST están en la documentación (en inglés).

08FAQrespuestas directas

Lo primero que preguntan los ingenieros.

¿Qué bases de datos admite Tenax?

Postgres 15, 16 y 17, incluido PostGIS, con respaldos completos físicos o lógicos y streaming de WAL. MySQL 8.4 con respaldos completos lógicos y streaming de binlog.

¿Hasta cuándo puedo restaurar, y con qué precisión?

Cualquier respaldo completo que conserven tus niveles de retención, y cualquier segundo dentro de la ventana de recuperación que fije tu política. La política de ejemplo prod mantiene una ventana de 7 días y 14 completos diarios, 8 semanales, 12 mensuales y 5 anuales.

¿Pueden Tenax o mi proveedor de almacenamiento leer mis datos?

El bucket no: cada objeto se cifra con age para tus destinatarios antes de subirlo. La consola lee los inventarios con una clave de catálogo que no puede descifrar los datos. Las restauraciones descifran con una identidad age que tú proporcionas; guarda una copia fuera de línea.

¿Qué pasa si un pod muere en mitad de un respaldo?

Nada que cuente. Un respaldo solo existe cuando su manifiesto está escrito, después de subir y verificar cada objeto. Una ejecución a medias no tiene manifiesto, así que nunca se ofrece para restaurar. La suite de caos mata a Tenax en pleno respaldo para demostrarlo.

¿Tenax monta volúmenes o usa snapshots?

No. Llega a las bases de datos solo por la red y sus pods no montan nada salvo emptyDir: ni hostPath, ni FUSE, ni iSCSI, ni dispositivos de bloque, y ningún PVC por defecto. Un driver de almacenamiento atascado no puede colgar un respaldo.

¿Qué necesita Tenax de mi base de datos?

Un usuario tenax de privilegios mínimos por base de datos, que nunca escribe, creado con el SQL de docs/sql/. Sin permisos de replicación, Tenax sigue haciendo respaldos lógicos y te dice qué conceder para restaurar a cualquier segundo.

¿Tenax es de código abierto?

Su código está disponible bajo The Republic Lex Aequa License 1.0. Cualquiera puede usarlo, modificarlo y redistribuirlo gratis, también en producción y en empresas comerciales, para proteger sus propios datos. Venderlo u ofrecerlo como servicio a terceros está reservado.

Prueba tu restauración antes de necesitarla.