Quickstart¶
This takes a Postgres database that already runs in your cluster from no backups to a tested restore in about ten
minutes. You need kubectl and helm access to the cluster, an S3-compatible bucket with credentials, and
age on your machine.
No cluster at hand?
Try the console and the CLI against a demo server first: one command, no Kubernetes, no database.
1. Install Tenax¶
helm install tenax oci://registry.example.com/helm/tenax --version 0.1.0 \
-n tenax-system --create-namespace \
--set image.repository=registry.example.com/tenax/tenax --set image.tag=v0.1.0 \
--set 'targetNamespaces={team-a}' \
--set examplePolicies.enabled=true --set examplePolicies.store=main
targetNamespaces lists the namespaces whose databases Tenax backs up; Tenax gets a Role there to read the
tenax-* credential secrets and nothing else. examplePolicies creates the shared prod, dev and critical
policies. Every value is described in Helm values.
2. Keys and the bucket¶
Generate the key pair that encrypts every backup. Keep an offline copy of tenax-backups.key: without it no
backup can be restored.
age-keygen -o tenax-backups.key # prints "Public key: age1…"
kubectl -n tenax-system create secret generic tenax-age-identity \
--from-literal=identity="$(grep AGE-SECRET-KEY tenax-backups.key)"
kubectl -n tenax-system create secret generic tenax-s3 \
--from-literal=accessKeyId=AKIA… --from-literal=secretAccessKey=…
apiVersion: tenax.republic.global/v1alpha1
kind: BackupStore
metadata:
name: main
spec:
s3:
endpoint: https://s3.us-east-1.amazonaws.com
region: us-east-1
bucket: db-backups
credentialsSecret: { namespace: tenax-system, name: tenax-s3 }
encryption:
ageRecipients: [age1qxyz…] # the public key from age-keygen
identitySecret: { namespace: tenax-system, name: tenax-age-identity }
kubectl apply -f store.yaml, then kubectl get backupstores: main turns Ready once Tenax reaches the bucket
and the keys parse.
3. The database user¶
Tenax connects with one user, tenax. Create it with the SQL in Database users, then store
its password next to the database:
kubectl -n team-a create secret generic tenax-postgres --from-literal=username=tenax --from-literal=password=…
For restore to any second, also let it replicate: add host replication tenax all scram-sha-256 to pg_hba.conf
and reload. You can do this later. Until then Tenax takes logical backups and its Replication condition says what
to add.
4. Declare the target¶
apiVersion: tenax.republic.global/v1alpha1
kind: BackupTarget
metadata:
name: app
namespace: team-a
labels:
tenax.republic.global/org: acme
tenax.republic.global/project: shop
tenax.republic.global/env: prod
spec:
engine: postgres
endpoint: { host: postgres, port: 5432 }
databases: [app]
policy: prod # nightly fulls, 7-day point-in-time window
credentials:
secretRef: { name: tenax-postgres }
checks:
- name: orders-not-empty
database: app
sql: select count(*) > 0 from orders
Within a minute Tenax starts a streamer for the target and takes the first full backup on its own
(reason chain-restart). If the user may not replicate yet, the target shows Replication=False with the fix
instead, and backups are logical until you apply it:
5. Restore, on purpose¶
tenax login --server https://tenax.example.com # or TENAX_SERVER + port-forward, see Console, CLI and access
tenax targets
tenax restore team-a/app --at "$(date -u -d '-10 min' +%FT%TZ)" --wait
tenax query <sandbox> -e "select count(*) from orders"
The restore runs your checks before it reports success. The sandbox deletes itself when its TTL expires. Next:
- Backups and the restore window explains what the window is and what keeps it unbroken.
- Verification turns this manual drill into a weekly, alerting one.
- Monitoring wires the alerts and the dashboard.