# Backups and restore

> Resta takes hourly snapshots of changed databases to a Docker volume or any S3-compatible bucket, and restores a database from the dashboard or API.

Source: https://resta.dev/docs/backups · Updated 2026-11-01

**Resta snapshots every database that changed, once an hour by default, and keeps 72 snapshots per database; snapshots go to a Docker volume unless you point them at an S3-compatible bucket.** You can also take a snapshot on demand and restore any snapshot from the dashboard or the API.

## The default does not survive losing the server

By default snapshots go to the `backups` volume, on the same disk as your data. That protects against mistakes: a bad migration, a deleted table. It does not protect against losing the disk or the server. For that, use a bucket with another provider.

## Send backups to a bucket

```dotenv
RESTA_BACKUP_TARGET=s3://my-bucket/resta
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1
```

For stores other than AWS S3, also set the endpoint:

| Provider | `RESTA_S3_ENDPOINT` | `AWS_REGION` |
|---|---|---|
| AWS S3 | (unset) | your bucket's region |
| Cloudflare R2 | `https://<account id>.r2.cloudflarestorage.com` | `auto` |
| Backblaze B2 | `https://s3.<region>.backblazeb2.com` | the B2 region |
| MinIO | your MinIO URL | `us-east-1` |

The credentials need `ListBucket` on the bucket, and `GetObject`, `PutObject` and `DeleteObject` on the prefix. Snapshots are stored at `<prefix>/dbs/<database id>/snapshots/<unix ms>.db.zst`.

## How snapshots work

- A snapshot is a consistent copy made with SQLite's `VACUUM INTO`. Writes keep going while it runs.
- It is compressed with zstd. Each snapshot is a complete database, not a delta.
- Each scheduled pass checks every database and snapshots only those whose files changed since their last snapshot. Idle databases cost nothing.
- The newest `RESTA_BACKUP_KEEP` snapshots per database are kept (72 by default; 3 days at hourly).
- Deleting a database deletes its snapshots.

## Choosing an interval

The interval is the most a restore can lose.

| `RESTA_BACKUP_INTERVAL_SECS` | Most lost on restore | Notes |
|---|---|---|
| `300` (5 minutes) | 5 minutes | More uploads; on large servers the pass must finish within 5 minutes. |
| `3600` (default) | 1 hour | |
| `86400` (1 day) | 1 day | Lowest load. Set `RESTA_BACKUP_KEEP` to the number of days you want. |

## Restore

In the dashboard: open the database, choose **Backups**, pick a snapshot, and type the database name to confirm. Or with the API:

```sh
curl -X POST https://db.example.com/v1/databases/user-42/restore \
  -H "Authorization: Bearer $RESTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"snapshot": 1793445870847}'
```

Restore downloads the snapshot and checks it (`PRAGMA integrity_check`) before touching the live database. Then it blocks new requests, waits for running ones, closes open transactions, replaces the file, and reopens. Changes made after the snapshot are lost. The database is unavailable for a few seconds.

## Back up Postgres too

Snapshots cover databases, not Postgres. Postgres holds the account, API keys and the list of databases. Back it up with Coolify's scheduled database backups, or:

```sh
docker compose exec -T postgres pg_dump -U resta resta | gzip > resta-meta-$(date +%F).sql.gz
```
