Self-hosting
Backup and restore
Because migrations are forward-only, your backup is your rollback. It is worth treating this page as a runbook rather than a reference.
What to back up
| Item | Why | Where it lives |
|---|---|---|
| The Postgres database | Everything: conventions, findings, receipts, audit log | Your database |
GARBOARD_SECRET_KEY (or your KMS key id) |
Without it, sealed data is permanently unopenable | Your secret store — not the database |
GARBOARD_RECEIPT_KEY |
Sign new receipts under the same identity | Your secret store |
| The GitHub App private key | Cheaper to rotate than restore, but it is not in the database | Your secret store |
The one people miss
A database backup with no key backup restores data you cannot decrypt.
GARBOARD_SECRET_KEY is the envelope root key that every per-organisation secret is sealed against — most visibly an organisation’s SSO client secret. It is deliberately not in the database, because a root key stored beside the data it protects protects nothing.
So a nightly pg_dump is not a backup of Garboard. It is a backup of half of Garboard, and the missing half is the half with no recovery path.
Back the key up when you set it, before you store anything with it, and verify you can read it back.
Restoring
- Restore the database.
- Restore the keys into the environment or secret store.
- Start the binary at the version that matches the schema — the same version that was running, or newer. Never older; see upgrades.
- Check the startup log.
store: applied migration NNNNlines tell you the binary migrated the restored schema forward. If you did not expect a migration, stop and check the version. - Verify
pgvector. Ifstore: episodic memory disabled (pgvector unavailable)appears, the extension is missing on the restored database and recall is silently off. - Sign in. This exercises the database write path, which is what
/healthzdoes not.
Testing a restore
/healthz returns ok on a badly restored install. It says ok on any version, and it stays green while new database connections fail — existing pooled connections keep working. It is not a restore test.
The cheapest real test is: sign in, open a repository, confirm its conventions are there with evidence, and export a receipt bundle and verify it offline. That exercises the database, the envelope key and the receipt key — the three things a restore can get wrong independently.
