Back up and restore
Back up the database and the secrets of a self-hosted deployment, restore them, and know what a restore loses or repeats.
Back up the database at least once a day, and always before an upgrade. scripts/backup.sh takes the backup, and Test a restore shows that it can be restored. The restore commands use the supplied compose file.
What to back up#
| What | How often | Why |
|---|---|---|
| The PostgreSQL database | All the time with point-in-time recovery if you can, or at least daily | It holds everything: settings, records, evidence, anchors and the audit log |
ENCRYPTION_KEY, SUBJECT_HASH_PEPPER, EVIDENCE_CHAIN_KEYS, RECEIPT_SIGNING_PRIVATE_KEY, RECEIPT_SIGNING_KEY |
Once, and after each rotation | Without them a restored database cannot be read, searched or verified |
Your .env and compose override files |
When they change | To rebuild the deployment |
The public key set from /sdk/v1/keys |
Once, and after a signing key change | To verify old receipts offline |
Redis needs no backup. It holds sessions and rate-limit counters.
Take a backup#
-
Set the database variables the API uses:
DB_HOST,DB_PORT,DB_USERNAME,DB_PASSWORDandDB_DATABASE. -
With the supplied compose file, set
DB_CONTAINER=piedap-consent-postgresas well. The script then runspg_dumpinside that container. -
Run the script from the repository.
Shell BACKUP_DIR=/srv/backups/consent scripts/backup.sh -
Save the public key set, the first time and after a signing key change.
Shell curl -s https://consent.example.in/sdk/v1/keys > keys-$(date +%F).json -
Encrypt the files and store them away from the host.
The script writes three files named after the database and the UTC time, such as piedap-consent-piedap_consent-20261009T013000Z.dump.
| File | What it holds |
|---|---|
.dump |
The database, in the custom format of pg_dump |
.dump.sha256 |
The SHA-256 of the dump |
.counts |
The rows of each table, counted in the same snapshot as the dump |
The password reaches PostgreSQL as PGPASSWORD and is never printed. To remove old backups, set BACKUP_KEEP_DAYS to the days to keep.
Restore#
-
Stop the API and the console, so nothing is written during the restore.
Shell docker compose stop consent-api consent-web -
Drop the database and create it again. An empty database holds nothing that a newer version added.
dropdbrefuses while another client is connected.Shell docker exec piedap-consent-postgres \ dropdb -U postgres --if-exists piedap_consent docker exec piedap-consent-postgres \ createdb -U postgres piedap_consent -
Restore the dump.
Shell docker exec -i piedap-consent-postgres \ pg_restore -U postgres -d piedap_consent \ < piedap-consent-piedap_consent-20260918T013000Z.dump -
Start an API image that matches the restored schema, or a newer one. A newer image applies its migrations on start.
Shell docker compose start consent-api consent-web -
Check that the API answers
200.Shell curl -s http://127.0.0.1:8080/health -
Sign in, open Audit. In Evidence anchors, choose Verify on the most recent days.
-
Choose Verify the log.
The restore worked when /health shows "database": "up", and the anchors and the log verify.
After a restore#
Tell your customers what was lost and what repeats.
- Consent recorded after the backup is gone. Customers who stored their
evidence.anchoreddeliveries hold anchors for days that are now missing or different. Tell them which period is affected. - Webhooks may be sent twice. Events that waited in the outbox at backup time go out again. Each event body has an
id. Receivers should ignore anidthey have already handled.