Skip to main content
Version: 0.9

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.