Tenax
PT
GitHub

Operador de backup e restauração para Kubernetes · Postgres 15–17 · PostGIS · MySQL 8.4

Segure firme cada segundo.

O Tenax faz backup dos bancos Postgres e MySQL que você roda no Kubernetes no seu próprio bucket S3 e transmite cada mudança assim que ela acontece. Escolha um segundo dentro da janela e receba exatamente aquele banco de volta em um sandbox que você pode consultar. Criptografado antes de sair do cluster e projetado para sobreviver a qualquer queda, por isso nunca trava.

Um binário, um chart. Seu bucket, suas chaves.

Janela de restauração pontual · team/app · política prod janela 7 dias · cadeia íntegra

Restaurar para
2026-10-10 14:02:37 UTC
Backup base
20261010T020712Z-4c1e
Depois reaplicar
114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --wait

Veja a volta no tempo ou arraste você mesmo pela janela. As marcas laranja são os backups completos de cada noite; a faixa ciano é o fluxo de mudanças entre eles. Ilustração de uma política prod: completo toda noite, janela de 7 dias.

  • 9spara restaurar um banco Postgres de 644 MiB em um sandbox: baixar, descriptografar, recuperar e verificar
  • 4.2×menor no bucket: um backup físico de 683 MiB armazenado como 161 MiB
  • 72MiB/sde vazão em um backup lógico completo com 4 vCPU, em streaming e sem arquivos temporários
  • 20msp99 da visão geral do console com 1.000 bancos e 100.000 backups

Medido com make test-perf e make test-ui no ambiente de testes k3d (4 vCPU, MinIO no cluster). Os números estão em docs/PERF.md no repositório.

01Como funcionacinco passos

De uma mudança ao vivo a um sandbox que você pode consultar.

Nenhum agente dentro do seu banco, nenhum snapshot de volume. O Tenax lê as mudanças pela rede, lacra os dados antes que saiam do pod e traz qualquer segundo de volta quando você pede.

  1. 01

    Transmitir

    Backups completos agendados e, além disso, cada mudança assim que acontece: o receptor de WAL do próprio Tenax para o Postgres e o mysqlbinlog para o MySQL.

    WAL · binlog
  2. 02

    Criptografar

    Cada byte é comprimido com zstd e criptografado com age para as suas chaves públicas, dentro do pod, em uma única passagem em streaming e sem arquivos temporários.

    zstd → age
  3. 03

    Guardar

    Uploads multipart em paralelo para qualquer bucket compatível com S3, um SHA-256 para cada objeto e o manifesto escrito por último.

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

    Restaurar no segundo

    Peça um horário ou um ID de backup. O Tenax escolhe o backup base e reaplica as mudanças até aquele segundo exato.

    --at 14:02:37
  5. 05

    Abrir um sandbox

    O resultado chega a um banco privado com TTL e um console de consultas somente leitura. Suas verificações rodam antes de a restauração contar como sucesso.

    --ttl 6h

02Restaurarquatro destinos

Restaure onde quiser, sem tocar na produção.

Uma restauração é um recurso do Kubernetes como qualquer outro. Peça um horário ou um ID de backup e escolha o destino. O Tenax planeja o backup base e as alterações a reaplicar, e roda suas verificações antes de considerar a restauração bem-sucedida.

SANDBOX

Uma cópia descartável

Um banco privado com TTL, credenciais e um console de consultas somente leitura. Ele se limpa sozinho.

--to sandbox --ttl 6h
NEW

Um banco novo

Restaurado e verificado em um sandbox intermediário, depois copiado com um novo nome. Objetos existentes nunca são sobrescritos.

--to new --database app_copy
IN PLACE

Voltar atrás de verdade

Faz antes um backup de segurança e pede que você digite o nome do alvo. Exige o papel restorer-admin.

--to in-place --confirm app
DOWNLOAD

Um arquivo de dump

Um dump lógico descriptografado e comprimido, atrás de um link que expira em uma hora. Auditado.

--to download

03Vejaum minuto

Um minuto, do início à restauração.

Comandos reais e o console real sobre um catálogo de demonstração: liste cada banco, veja o que mudou durante a noite e restaure em um sandbox.

0:58 · com som · 1,9 MB

O console

Visão geral do console do Tenax: nove bancos com o último backup completo, janela de restauração, atraso, tamanho e saúde
Todos os bancos de relance: último backup completo, janela de restauração, atraso do fluxo, tamanho e saúde.
Página de um banco no console do Tenax: a linha do tempo restaurável, tendências de linhas e tamanho, e a lista de versões com selos de verificação
Um banco: sua linha do tempo restaurável, suas tendências e cada versão, com as restaurações verificadas marcadas.
Diff entre dois backups no console do Tenax: uma coluna adicionada, uma tabela esvaziada, linhas e tamanho por tabela
Duas versões lado a lado: o que foi aplicado, quais colunas mudaram, linhas e tamanho por tabela.

Saiba o que mudou entre duas versões.

Cada backup traz um inventário das tabelas, linhas e tamanhos. Cada segmento de alterações traz um resumo do que fez. Assim, antes de restaurar qualquer coisa, você vê qual versão ainda tinha as linhas perdidas.

  • Diffs entre quaisquer dois backups. Mudanças de esquema, contagem de linhas e tamanho por tabela.
  • Resumo de alterações por segmento WAL ou binlog. Inserções, atualizações, exclusões e DDL por tabela, para achar fácil um TRUNCATE às 22:48.
  • Prévias mascaradas. Linhas de amostra com as colunas que você indicar mascaradas, legíveis com uma chave de catálogo que não descriptografa os dados.

A CLI

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

Saída do servidor de demonstração do repositório: go run ./hack/demo e aponte a CLI ou um navegador para ele.

Console, CLI e API

Uma API JSON descrita em OpenAPI, com papéis do Cloudflare Access. Toda escrita e toda consulta são auditadas.

Nativo de GitOps

Stores, políticas, alvos e restaurações são CRDs. Níveis de retenção, alertas e um dashboard do Grafana vêm com o chart.

Verificado, não presumido

Restaurações de verificação agendadas rodam suas consultas SQL em uma restauração real e marcam o backup como verificado. O tempo de restauração vira métrica.

04Confiabilidadeà prova de quedas

Nunca trava. Nem um kill -9 o abala.

Backups falham de madrugada de jeitos chatos: um mount travado, um pod morto, um arquivo pela metade. O Tenax foi feito para que nenhum deles o trave ou se passe por um backup.

Nunca trava

Sem montagens nem dispositivos de bloco; bancos só pela rede. Cada fluxo tem um watchdog de progresso e cada ferramenta roda em seu próprio grupo de processos.

Crash-only

Mate qualquer pod a qualquer momento. Um backup só existe quando o manifesto é gravado por último, e a suíte de caos derruba o Tenax no meio do backup para provar.

Os dados trafegam em Jobs, nunca no controlador.

O controlador apenas agenda. Movers e streamers levam os bytes por um único pipeline em streaming direto para o seu bucket, e o restorer os traz de volta.

Gentil com a produção

Um único usuário de banco com privilégio mínimo, que nunca escreve. Slots por host, limites de banda e uma proteção de carga que adia o backup quando o servidor está ocupado.

Streaming de alterações

O receptor WAL do próprio Tenax em um slot de replicação para Postgres e mysqlbinlog para MySQL. A janela chega ao presente, mesmo com o banco ocioso.

Rápido por construção

Pipelines em streaming sem arquivos temporários, uploads multipart em paralelo, zstd multithread e um catálogo em memória por trás de um console que responde em milissegundos.

Leve a ponto de você esquecer que está lá.

O Tenax é um único binário Go. O controlador e a API apenas agendam e leem; os dados trafegam em Jobs de vida curta que terminam ao concluir, então entre um backup e outro nada pesado fica no seu cluster.

54MBum binário estático: controlador, API e console, movers, streamers, restorer e CLI
18MBde download comprimido, sem runtime para instalar nem sidecars
21MiBde memória do controlador em repouso, observando um cluster de bancos
19MiBde memória da API e do console em repouso, com o catálogo em memória

Working set informado pelo kubelet no cluster de testes k3d (12 bancos) após as suítes end-to-end e de caos; a CPU em repouso fica em poucos milicores. Cada banco com restauração pontual também roda um streamer, cerca de 70 MiB durante o streaming.

05Segurançacriptografado na origem

Seu bucket só vê dados criptografados.

A criptografia acontece no pod, antes do upload, com chaves que o Tenax não guarda. O acesso é dividido por papéis, e cada escrita e cada consulta deixa um registro de auditoria.

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

Cada objeto de dados é um arquivo age criptografado para os seus destinatários (X25519). Só o manifesto é JSON em texto claro, e ele não contém segredos.

  • Criptografado antes de sair

    Cada objeto é criptografado no cliente com age para as suas chaves. O bucket nunca vê texto puro, e a chave de catálogo do console não lê dados.

  • Uma chave de catálogo que não lê dados

    O console lê inventários e prévias mascaradas com uma identidade de catálogo separada. Ela não consegue descriptografar dumps, backups base, WAL nem binlogs.

  • Verificado de ponta a ponta

    O manifesto guarda um SHA-256 para cada objeto armazenado, conferido ao fim de cada leitura, e é escrito por último com um PUT condicional.

  • Segredos longe dos olhos

    As credenciais chegam às ferramentas filhas por arquivos lacrados em memória, nunca por argv nem pelo ambiente. Os logs ocultam tudo o que parece segredo.

  • Papéis e trilha de auditoria

    Login pelo Cloudflare Access e quatro papéis, de viewer a restorer-admin. Restaurar no lugar exige o papel mais alto e uma confirmação digitada.

  • Pods blindados

    Sem root, sistema de arquivos raiz somente leitura, todas as capabilities removidas e seccomp RuntimeDefault. Os pods de dados montam apenas emptyDir.

06Comparadoa um cron

Mais do que um dump noturno em um bucket.

Quando você precisa…script cron + pg_dumpTenax
Voltar para 09:41:27O dump da noite passada, até 24 h perdidasQualquer segundo da janela
Provar que um backup restauraDescobrir durante o incidenteRestaurações de verificação agendadas com suas checagens
Achar a versão com as linhas perdidasRestaurar várias e olharDiffs e resumos de alterações no console
Olhar sem riscoRestaurar sobre um staging compartilhadoUm sandbox privado com TTL
Manter o bucket às cegasCriptografia no servidor, se configuradaCriptografia age no cliente com suas chaves
Sobreviver a um nó que cai no meioUm arquivo pela metade que parece completoNada conta até o manifesto ser gravado por último
Saber que continua funcionandoSilêncioAlertas de backups atrasados, cadeias quebradas e atraso

07Instalarcerca de dez minutos

Do zero ao restaurável.

  1. Instale o chart

    CRDs, o controlador, a API e o console, RBAC, alertas e o 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. Aponte para um bucket e suas chaves

    Qualquer armazenamento compatível com S3. Gere uma chave age com age-keygen e guarde uma cópia 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…]   # só a chave pública; a privada fica offline
  3. Declare um alvo ao lado do banco

    Crie o usuário tenax com o SQL de docs/sql/ e faça commit do alvo no 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 toda noite, janela de 7 dias
      credentials:
        secretRef: { name: tenax-postgres }
  4. Restaure algo, de propósito

    O primeiro backup completo começa sozinho. Depois teste aquilo que você espera nunca precisar.

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

O guia completo, cada campo dos CRDs e a API REST estão na documentação (em inglês).

08FAQrespostas diretas

O que os engenheiros perguntam primeiro.

Quais bancos de dados o Tenax suporta?

Postgres 15, 16 e 17, incluindo PostGIS, com backups completos físicos ou lógicos e streaming de WAL. MySQL 8.4 com backups completos lógicos e streaming de binlog.

Até quando posso restaurar, e com que precisão?

Qualquer backup completo que seus níveis de retenção mantenham, e qualquer segundo dentro da janela de recuperação definida pela sua política. A política de exemplo prod mantém uma janela de 7 dias e 14 completos diários, 8 semanais, 12 mensais e 5 anuais.

O Tenax ou meu provedor de armazenamento conseguem ler meus dados?

O bucket não: cada objeto é criptografado com age para os seus destinatários antes do upload. O console lê os inventários com uma chave de catálogo que não descriptografa os dados. As restaurações descriptografam com uma identidade age fornecida por você; guarde uma cópia offline.

O que acontece se um pod morrer no meio de um backup?

Nada que conte. Um backup só existe quando o manifesto é escrito, depois que cada objeto foi enviado e verificado. Uma execução pela metade não tem manifesto e nunca aparece para restauração. A suíte de caos mata o Tenax no meio do backup para provar isso.

O Tenax monta volumes ou usa snapshots?

Não. Ele acessa os bancos só pela rede, e os pods não montam nada além de emptyDir: nada de hostPath, FUSE, iSCSI ou dispositivos de bloco, e nenhum PVC por padrão. Um driver de armazenamento travado não consegue travar um backup.

Do que o Tenax precisa no meu banco?

Um usuário tenax de privilégio mínimo por banco, que nunca escreve, criado com o SQL em docs/sql/. Sem permissão de replicação o Tenax ainda faz backups lógicos e diz o que conceder para restaurar em qualquer segundo.

O Tenax é open source?

O código está disponível sob a The Republic Lex Aequa License 1.0. Qualquer pessoa pode usar, modificar e redistribuir sem custo, inclusive em produção e em empresas comerciais, para proteger os próprios dados. Vender ou oferecer como serviço a terceiros é reservado.

Teste sua restauração antes de precisar dela.