Backups
By Rafael Duarte · created 17 Jan 2022, 13:10 UTC · last updated 13 Oct 2026, 09:05 UTC · version 12
backupsoperationsregionsdata-residency
Backups are written nightly to the customer's region and kept for 35 days.
That sentence is what we promise customers, and everything below is how we keep it.
What is backed up
| What | How | When | Kept |
|---|---|---|---|
| identity-service database (PostgreSQL on RDS) | RDS snapshot, plus a logical dump to S3 | 01:00 and 01:30, region's local time | 35 days |
| notifier database (in-app notices, handled events) | RDS snapshot | 01:00 | 35 days |
| Files (uploaded SAML metadata, people and depot import files, data exports) | S3 versioning in the region's file bucket | on every change | 35 days of versions |
| Redis | not backed up | – | queues and cache only; rebuilt on start |
The dumps go to the region's backup bucket (acme-backups-eu, acme-backups-uk, acme-backups-us). Everything is encrypted with AES-256 under a KMS key that belongs to that region and is rotated every year. Objects are locked for 35 days so nobody, us included, can delete them early; a lifecycle rule deletes them after that.
Nothing leaves the region
- Backups for an EU client are written in Frankfurt and only in Frankfurt. The same for London and Virginia.
- Cross-region replication is turned off on the backup buckets, and an account policy refuses it.
- Restores happen in the same region. If someone asks for a copy in another region "just for testing", the answer is no.
Recovery targets
- Recovery point: 24 hours. At worst we lose what happened since the last nightly backup.
- Recovery time: 8 hours to have a region's service running again from backups.
Both are in our contracts and our Disaster recovery plan. Restores are tested every quarter: see Restore drills.
Restoring
Open an OPS ticket first. Two people, one runs the commands, one checks.
A whole database (corruption, a bad migration):
- Stop the region's identity-service workers so nothing writes.
- Restore the latest good RDS snapshot to a new instance in the same region (
restore-<date>). - Check row counts and the newest
updated_atagainst the dump's manifest. - Point identity-service at the new instance, start the workers, run the smoke tests.
- Post on the status page before you start and when you finish.
One client's data (a customer deleted something by mistake):
- Restore the dump to a temporary instance in the same region.
php artisan client:extract --client=<id> --from=<instance>writes that client's rows to a restore schema.- Agree with the customer, through support, what to bring back. Then
client:apply-restore. - Delete the temporary instance the same day.
Alarms
- Backup job failed or didn't run: pages the on-call engineer the same morning.
- Dump smaller than 80 % of the night before: alarm in #ops.
For customers and questionnaires
"Nightly, kept 35 days, in your region; a restore is tested each quarter; recovery point 24 hours, recovery time 8 hours." Sales and support can quote that.
13 October: Halden Foods' question 15 asks where backups are kept, not only the live data. Answer: in the customer's region; for an EU customer like Halden, in Frankfurt with the rest. Mira gave the same answer on ACM-296.
Change history
| Version | Date | Who | What |
|---|---|---|---|
| 8 | 20 Jan 2025 | Rafael | UK region bucket |
| 9 | 19 Jan 2026 | Rafael | US region bucket |
| 10 | 8 Jul 2026 | Oskar | Object lock; restore steps rewritten |
| 11 | 28 Sep 2026 | Oskar | Recovery targets; link to the restore drill log |
| 12 | 13 Oct 2026 | Oskar | Backups stay in the region (Halden's question 15) |