---
title: "Use backups and restore drills in your app"
description: "Declare how much data you can lose and how fast you must restore, and prove it with timed restore drills."
section: "guides"
status: "Building for 1.2"
spec: "backup-restore-evidence"
canonical: "https://mainsdk.dev/docs/guides/use-backup"
updated: "2026-10-28"
---

# 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: Building for 1.2 · Canonical: https://mainsdk.dev/docs/guides/use-backup · Updated: 2026-10-28

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](https://mainsdk.dev/docs/getting-started/thirty-minutes)).
- 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.

```yaml title="main.yaml"
backup:
  rpo: 15m
  rto: 1h
  keep: 35d
  drill: { every: 30d, to: scratch }
  driver: ./backup.ts
  provider:
    pitr-window: 7d
    pitr-granularity: 1m
    offsite: true
```

`backup.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.

```ts title="backup.ts"
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

```bash
npx mainsdk drill restore --at "2027-07-01T10:00:00Z" --to scratch
```

The 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.
