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.
- 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.
- 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 - 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 - 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 - 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 - 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.
Una copia desechable
Una base privada con TTL, credenciales y una consola de consultas de solo lectura. Se limpia sola.
--to sandbox --ttl 6hUna 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_copyVolver 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 appUn archivo de volcado
Un volcado lógico descifrado y comprimido tras un enlace que caduca en una hora. Auditado.
--to download03Verloun 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.
La consola
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
$ 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 -
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.
- 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
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.
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.
$ 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_dump | Tenax |
|---|---|---|
| Volver a las 09:41:27 | El volcado de anoche, hasta 24 h perdidas | Cualquier segundo de la ventana |
| Demostrar que un respaldo se restaura | Descubrirlo durante el incidente | Restauraciones de verificación programadas con tus comprobaciones |
| Encontrar la versión con las filas perdidas | Restaurar varias y mirar | Diffs y resúmenes de cambios en la consola |
| Mirar sin riesgo | Restaurar sobre un staging compartido | Un sandbox privado con TTL |
| Que el bucket no vea nada | Cifrado en el servidor, si se configuró | Cifrado age en el cliente con tus claves |
| Sobrevivir a un nodo que muere a mitad | Un archivo a medias que parece completo | Nada cuenta hasta que el manifiesto se escribe al final |
| Saber que sigue funcionando | Silencio | Alertas por respaldos atrasados, cadenas rotas y retraso |
07Instalarunos diez minutos
De cero a restaurable.
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}'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
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 }
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.