Backups & recovery
Surplus backs up both its database and its uploaded files automatically, on a daily schedule, to storage that is separate from the live data.
Database backups
The postgresBackup cron worker runs once daily (0 5 * * *). It takes a logical dump of the primary database with pg_dump, compresses it, and uploads it to a private S3 backups bucket — separate from the bucket that holds live application files.
Neon, the managed Postgres provider, additionally offers its own point-in-time recovery capabilities at the platform level; the daily logical dump is an independent, portable copy on top of that.
File-storage backups
The fileStorageBackup cron worker runs once daily (0 5 * * *). It mirrors the primary file-storage bucket to a separate backup bucket — copying new and changed objects and removing orphans — so every uploaded file has a second, independent copy.
Where backups live
Backups are written to dedicated, private buckets that are distinct from live storage. Access to backup paths is also explicitly blocked from the authenticated file-serving endpoint, so backup objects can never be retrieved through the application API.
How the jobs run
Both jobs are independent Bun programs deployed as their own Railway services, scheduled with cron and configured to run to completion (restartPolicyType: NEVER) rather than staying resident. They reuse the platform's shared packages for database and storage access. See Cron workers.
What still needs to be documented
Backups exist and run daily; the surrounding recovery policy is an operational matter being formalized:
- Retention period for database and file backups.
- Restore procedure and testing cadence — how a restore is performed and how often it's rehearsed.
- Recovery objectives — target recovery time (RTO) and recovery point (RPO).
These are tracked as open items rather than stated here, so the page reflects what is actually implemented.