Use backups and restore drills in your app
Declare how much data you can lose and how fast you must restore, and prove it with timed restore drills.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
You will declare your RPO and RTO, let check compare them with what your provider keeps, and run a restore drill that leaves signed evidence. It takes about 15 minutes.
You need
- A MainSDK app with a
main.yaml(see Thirty-minute path). - A database provider with backups on, such as Neon, Supabase or AWS Backup.
1. Declare your targets
Add the block to main.yaml. rpo is the most data you can lose, rto the longest a restore may take, keep how long restore points are kept.
backup:
rpo: 15m
rto: 1h
keep: 35d
drill: { every: 30d, to: scratch }
driver: ./backup.ts
provider:
pitr-window: 7d
pitr-granularity: 1m
offsite: truebackup.provider states what your provider is set to. npx mainsdk check compares it with the targets and fails with the provider setting to change when it cannot meet them (MAIN-BACKUP-0001 for RPO, 0002 for retention).
2. Give the drill a driver
backup.driver names a module whose default export restores a point in time into a new scratch branch or database. MainSDK never runs or stores backups itself.
import type { BackupDriver } from "mainsdk/backup";
import { neonBackupDriver, type ProviderCall } from "mainsdk/backup-providers";
declare const call: ProviderCall; // your read and branch calls to the Neon API
declare function countRows(branch: string): Promise<Record<string, number>>;
declare function countRowsAt(at: Date): Promise<Record<string, number>>;
const driver: BackupDriver = neonBackupDriver({
project: "my-project",
production: "br-production", // a drill never restores into this branch
call,
fingerprint: (branch) => countRows(branch),
expected: (at) => countRowsAt(at),
});
export default driver;3. Run a drill
npx mainsdk drill restore --at "2027-07-01T10:00:00Z" --to scratchThe drill restores into scratch, times the restore against rto, compares row counts or checksums with what was expected, deletes the scratch environment and signs the result to .mainsdk/evidence/. A restore that lands in production is refused with MAIN-BACKUP-0004.
4. Use the evidence
mainsdk assure --standard soc2 passes A1.2, A1.3 and CC7.5 with a drill:<digest> reference when a signed drill passed in the last 30 days. The trust badge turns amber with "restore drill overdue" after 30 days without one, and mainsdk adopt finish prints that line in the launch report.
Check it worked
Run npx mainsdk check. It passes when backup is configured as shown and the provider's settings meet your targets.
Was this page helpful?