Skip to main content
Version: 0.11

Backup and restore

What to back up​

PostgreSQL is the only stateful component in a TokenTimer deployment. The API, dashboard, and workers are stateless; everything they need (tokens, workspaces, users, alert queue, audit and delivery logs, system settings) lives in the database.

That means a consistent database dump is a complete backup. Also keep a copy of your deployment configuration (.env for Compose, values file and Kubernetes Secrets for Helm) so you can rebuild the stack, but treat secrets according to your usual secret-handling policy rather than bundling them with data backups.

note

Session cookies are signed with SESSION_SECRET. If you restore to a new environment with a different secret, users simply log in again; no data is lost.

Docker Compose​

The Compose stack runs PostgreSQL as the postgres service (container tokentimer-db), with database and user both named tokentimer by default.

Backup​

docker compose exec postgres pg_dump -U tokentimer tokentimer > backup.sql

For scheduled backups, add a timestamp and compress:

docker compose exec postgres pg_dump -U tokentimer tokentimer | gzip > "tokentimer-$(date +%F).sql.gz"

Restore​

docker compose exec -T postgres psql -U tokentimer tokentimer < backup.sql
warning

Restore into an empty database. If the target database already contains data, drop and recreate it first (or restore into a fresh volume), otherwise the restore can fail on conflicts or leave a mixed state.

If you changed DB_USER or DB_NAME in your .env, use those values in the commands above.

Kubernetes (Helm / CloudNativePG)​

The Helm chart provisions PostgreSQL as a CloudNativePG Cluster named <release>-pg by default (for a release named tokentimer, the cluster and its pods are tokentimer-pg).

If you enabled the chart's backup support (postgresql.cloudnative.backup.enabled), CloudNativePG handles base backups and WAL archiving to object storage (barman object store with S3 credentials and a retention policy). Prefer this for production: it gives you point-in-time recovery and does not depend on manual dumps.

Trigger an on-demand backup with a CNPG Backup resource, and restore by bootstrapping a new cluster from the object store (see the CloudNativePG recovery documentation for your operator version).

Option B - Manual pg_dump against the cluster pod​

For ad-hoc dumps or when CNPG backups are not configured, exec into the current primary pod:

# Find the primary pod of the CNPG cluster
kubectl get pods -n tokentimer -l cnpg.io/cluster=tokentimer-pg

# Dump (adjust pod name to the current primary, e.g. tokentimer-pg-1)
kubectl exec -n tokentimer tokentimer-pg-1 -- \
pg_dump -U tokentimer tokentimer > backup.sql

Restore into an empty database:

kubectl exec -i -n tokentimer tokentimer-pg-1 -- \
psql -U tokentimer tokentimer < backup.sql

External PostgreSQL​

If you pointed the chart at an external database (postgresql.external), use your existing backup tooling for that instance; TokenTimer adds no extra state outside it.

PVCs on uninstall​

helm uninstall retains the CloudNativePG PVCs by default. Your data survives an accidental uninstall, but a retained PVC is not a backup: it lives on the same storage as the cluster. Delete retained PVCs manually only once you have confirmed backups elsewhere.

Recommended cadence and retention​

EnvironmentCadenceRetention
ProductionDaily automated dump or continuous CNPG WAL archiving30 days, plus monthly archives per your compliance needs
Staging / labWeekly, or before risky changes7 days

Whatever the schedule, periodically test a restore into a scratch environment. A backup you have never restored is not verified.

Before every upgrade​

Always take a backup before major upgrades or database migrations:

# Compose
docker compose exec postgres pg_dump -U tokentimer tokentimer > pre-upgrade-backup.sql

# Kubernetes (CNPG)
kubectl exec -n tokentimer tokentimer-pg-1 -- \
pg_dump -U tokentimer tokentimer > pre-upgrade-backup.sql

Then follow the upgrade steps in the install runbook.