Browse the docs
Self-hosting & operations

Backup & restore

Back up three things - Postgres, object storage and keys - encrypt them with keys you control, and rehearse the restore before you ever need it.

A backup is only as good as the last time you restored from it. This page covers what an OrthID deployment stores, how to capture it on a schedule, and a step-by-step restore you should run on a regular basis against a throwaway environment.

What to back up

An OrthID deployment has three pieces of durable state:

  • Postgres. The system of record - users, organisations, sessions, RBAC, and the audit log. Protected by row-level security, so a logical dump preserves the tenant boundaries.
  • Object storage. Larger blobs that do not live in Postgres: profile media, exported audit archives and signed document bundles.
  • Keys. The signing and encryption key material. With BYOK this lives in your own KMS or HSM and you back it up there; without it, you must export the OrthID-managed keystore.
A database backup is useless without its keys
Records are encrypted at rest. Restoring Postgres without the matching keys gives you ciphertext you cannot read. Always capture the keystore in the same backup window as the database, and store it separately with stricter access. See BYOK.

Back up Postgres

Use a logical dump for portability across versions. Run it against a read replica where possible so production write traffic is undisturbed.

Terminal
# Logical, compressed dump of the OrthID database
pg_dump \
  --format=custom \
  --no-owner \
  --dbname="$ORTHID_DATABASE_URL" \
  --file="orthid-$(date +%Y%m%d-%H%M).dump"

Encrypt with your own keys

OrthID never asks you to hand it a backup key. Encrypt the dump and the exported keystore with a key you control before either leaves the host, then ship the ciphertext to off-site storage in the same region as the deployment.

Terminal
# Encrypt the dump to your team's public key, then upload
age -r "$BACKUP_AGE_RECIPIENT" \
  -o orthid-backup.dump.age orthid-20260622-0200.dump

aws s3 cp orthid-backup.dump.age \
  "s3://orthid-backups-au-syd-1/$(date +%Y/%m/%d)/" \
  --sse aws:kms --sse-kms-key-id "$BACKUP_KMS_KEY"

Scheduled backups

Run backups as a Kubernetes CronJob in the deployment namespace. Keep a sensible retention window - for example nightly fulls held for 30 days, with a weekly copy held for a year for compliance.

backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: orthid-backup
  namespace: orthid
spec:
  schedule: "0 2 * * *"        # 02:00 daily, in-region
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: backup
              image: orthid/backup:2.7.0
              args: ["dump", "--encrypt", "--upload"]
              envFrom:
                - secretRef:
                    name: orthid-backup-env

Restore procedure

Restore is the part you must rehearse. The steps are deliberate so that a real incident is a checklist, not an improvisation:

  1. Provision a clean Postgres and an empty deployment in the region.
  2. Restore the keystore first, so decryption is available.
  3. Decrypt and restore the most recent good Postgres dump.
  4. Restore object storage to the matching bucket.
  5. Point OrthID at the restored database and start it.
  6. Verify: sign in, verify a token, read the audit log, and confirm decrypted fields render.
Terminal
# Decrypt, then restore into a fresh database
age -d -i backup-key.txt orthid-backup.dump.age > orthid-restore.dump

pg_restore \
  --no-owner \
  --clean --if-exists \
  --dbname="$ORTHID_RESTORE_URL" \
  orthid-restore.dump
A backup you have not restored is a guess
Schedule a restore drill - monthly is a good cadence. Restore into a throwaway namespace, run the verification checks above, and record the result. Untested backups fail exactly when you need them most.

Next steps

  • Upgrade - take a backup before every upgrade.
  • BYOK - keep the encryption keys your backups depend on in your own KMS or HSM.
  • Regions - keep backups in the same sovereign region as the deployment.