Documentation for v2.11.0. Read the latest, v2.12.0.

Operator

Backup and restore

Sanshain keeps everything in one database, so a backup is a copy of that database and a restore is putting the copy back. How the copy is taken depends on the engine: on SQLite the service produces it, on PostgreSQL pg_dump does.

What changed in 2.6.0

  • GET /admin/backup/sqlite streams a consistent snapshot while the service runs, with a button on the admin page.
  • The Helm chart's backup.* values render a nightly CronJob for either engine; Kustomize users get the same job as commented examples.

What is in the database#

Specs and their endpoint snippets, version lines, dependencies, branches and timeline, users, password hashes, sessions, API tokens, the authentication configuration including the LDAP bind password and the OIDC client secret, webhook secrets and the delivery log, the audit log, every setting. A backup is as sensitive as the instance; keep it where only operators can read it.

SQLite#

Do not copy sanshain.db from the volume while the service runs: with the write-ahead log open a file copy can be inconsistent. Sanshain produces the copy itself:

curl -fsS -H "Authorization: Bearer $SANSHAIN_TOKEN" \
  -o sanshain-backup.db "$SANSHAIN/admin/backup/sqlite"

The same snapshot is Download database snapshot under Database Configuration on the admin page.

The Helm CronJob#

With backup.enabled the chart renders one CronJob that fetches the snapshot to a backup volume and keeps the newest retain files:

backup:
  enabled: true
  schedule: "0 2 * * *"            # cluster time zone
  retain: 7
  # existingClaim: nightly-backups  # otherwise the chart creates a 5Gi claim
  tokenSecret:
    name: sanshain-backup-token     # kubectl create secret generic sanshain-backup-token --from-literal=token=<api token>
    key: token
Value Default Effect
backup.enabled false Render the CronJob and, without existingClaim, its claim.
backup.schedule "0 2 * * *" Cron schedule; concurrencyPolicy: Forbid.
backup.engine "" sqlite or postgres; empty derives it from secrets.databaseUrl (postgres:// or postgresql:// means PostgreSQL).
backup.retain 7 Files kept on the volume; older ones are deleted after each successful run. Below 1 the chart refuses to render.
backup.existingClaim "" Mount this claim instead of creating one.
backup.storage.size, storageClass, accessMode 5Gi, "", ReadWriteOnce The claim the chart creates.
backup.tokenSecret.name, key "", token SQLite only: the Secret holding the API token. Required; the chart fails without it.
backup.images.curl, backup.images.postgres curlimages/curl:8.11.1, postgres:17-alpine The job images for the two engines.

The SQLite job calls the service inside the cluster (http://<release>:<service.port>/admin/backup/sqlite), writes /backup/sanshain-<UTC timestamp>.db through a .part file, then prunes. Kustomize users find the same job, with its claim and the Secret command, as a commented example in deploy/kubernetes/backup-cronjob-sqlite.yaml; uncomment it and add the file to resources.

Restore#

  1. Scale Sanshain to zero (kubectl scale statefulset sanshain --replicas=0).
  2. On the data volume replace sanshain.db with the snapshot and delete sanshain.db-wal and sanshain.db-shm if present.
  3. Scale back up. The migration runner brings the snapshot's schema up to the running version, so a snapshot from an older instance version restores; a snapshot from a newer version does not.

PostgreSQL#

pg_dump --format=custom --file=sanshain.dump "$DATABASE_URL"

With Helm, the same backup section renders a pg_dump CronJob when the engine is postgres; it reads DATABASE_URL from the chart's own Secret, so no further credential is needed, and writes /backup/sanshain-<UTC timestamp>.dump:

backup:
  enabled: true
  schedule: "0 2 * * *"
  retain: 7

Kustomize: deploy/kubernetes/backup-cronjob-postgres.yaml, commented like the SQLite one.

Restore#

  1. Scale Sanshain to zero.
  2. pg_restore --clean --if-exists --dbname="$DATABASE_URL" sanshain.dump
  3. Scale back up; migrations run on start as above.

Before an upgrade or a reset#

Take a backup before changing the image tag and before anything in the Danger Zone: migrations are forward-only and the resets have no undo. See Upgrades and migrations.

Checked against v2.6.0.