Backup
A complete, restorable backup of a Server is three separate things. Two of them are not in the database dump, and the operator decides where each lives.
| What | Where it lives | In the DB dump? |
|---|---|---|
| The PostgreSQL database — records, the audit_vault chain, the signing-key registry, provisioning state | Postgres | Yes |
| The customer-held encryption keys (encrypted mode) | Wherever you keep them — the Server never stores them | No |
| The config-as-code provisioning files | Your PROVISIONING_CONFIG_PATH directory / source control | No |
The split is the point. A database dump without the encryption keys is opaque-but-safe — an attacker who steals it cannot read encrypted payloads. The encryption keys without the database are useless. Back up each on its own schedule, to its own place.
This runbook pairs with the recovery runbook — a backup you have never restored is a hope, not a backup.
Back up the database
deploy/scripts/backup.sh is the supported path. It writes a timestamped, compressed tarball and
prunes to the last --keep N (default 7):
./deploy/scripts/backup.sh # keep last 7
./deploy/scripts/backup.sh --keep 30
BACKUP_DIR=/mnt/backups ./deploy/scripts/backup.sh
Each tarball contains two files. Under the hood the script runs pg_dump in custom format against
the bundled postgres (or your external DATABASE_URL for Aurora/RDS):
db.dump # pg_dump -Fc — the full database, custom format
vault-public-keys.csv # the signing-key registry, PUBLIC keys only
db.dump size: 740K
The vault-public-keys.csv is public-key metadata only — fingerprints, algorithms, status,
activation and retirement dates. Private signing keys are never written to the database and never
appear in a backup:
key_id,public_key,algorithm,status,activated_at,retired_at
6a639248683aab56,MCowBQYDK2VwAyEA3GG...,Ed25519,active,2026-05-26 01:24:53+00,
affc2b9bfb22144e,MCowBQYDK2VwAyEAV5Q...,Ed25519,retired,2026-05-26 01:08:47+00,2026-05-26 01:24:53+00
It is there so that after a restore you can confirm the registry came back intact (see the recovery runbook). The active and retired keys are listed, because retired keys still verify records signed before a rotation.
For an external database (Aurora, RDS, Cloud SQL), back up with your provider's snapshot mechanism
instead — backup.sh detects an external DATABASE_URL and uses pg_dump directly. The three-way
split above is unchanged.
Back up the chain off-box, too
Keep a second, database-independent copy of the chain: the NDJSON dump the offline verifier consumes. This is not a replacement for the database backup — it is the artifact you hand an auditor, and the copy that proves intact without a running Server.
DATABASE_URL=postgresql://… pnpm vault:dump ./chain-backup
{
"outDir": "./chain-backup",
"counts": { "audit_vault": 5, "vault_checkpoints": 0, "vault_signing_keys": 2, "org_admin_reads": 0, "org_admin_reads_checkpoints": 0 }
}
It writes five NDJSON files (see the audit runbook for the contents)
including vault_signing_keys.ndjson — the public-key registry travels with the dump, so the chain
verifies offline with no further inputs. Keep it alongside the db.dump tarball, not instead of it:
the database backup is what you restore a running Server from; the NDJSON dump is what proves the
chain to someone who does not trust your Server.
What a backup does not contain
- Private signing keys. Held in
VAULT_SIGNING_KEY(andVAULT_SIGNING_KEY_PREVIOUS) — your secret store, your responsibility. Back these up with your secrets, not your database. Without them a restored Server cannot sign new records (though existing records still verify against the public registry). - Customer encryption keys. In encrypted mode, the keys that decrypt payloads never reach the Server. They are not in any backup the Server can produce.
- Provisioning YAML. Keep
PROVISIONING_CONFIG_PATHunder source control; it is config, not data.
Validated against API v0.25.4 on 2026-05-25.