# Backups and restore

> Hourly snapshots of every app's data, restored from the dashboard, and copied encrypted to a bucket once you set one.

Your server snapshots every app's data folder each hour into a restic repository on its disk. You restore one project at a time from the dashboard.

```diagram
[Your app] its data folder
-> every hour
[Your server] a restic repository on its disk
-> encrypted copy, once configured
[Bucket] any S3-compatible storage
```

## What is saved

- **Each app's data folder**, `DATA_DIR`. Code and public files come back with the next deploy.
- **SQLite databases**, copied consistently while the app writes.
- **The server's own state**: secrets, access lists and Caddy's settings, restored by hand only.

The server keeps the newest snapshot of each of the last 24 hours, 7 days and 4 weeks, plus the last 3 taken before a restore. To leave an app out, write `"backup": false` in its `sitesolide.json`.

## Back up a database server

A running PostgreSQL or MongoDB fails the snapshot rather than being copied file by file. Declare a backup command on its service:

```json
"postgres": {
  "start": "/bin/sh /srv/sites/shop/app/postgres.sh",
  "port": 3081,
  "internal": true,
  "backup": { "folder": "postgres", "command": "/bin/sh /srv/sites/shop/app/postgres-backup.sh" }
}
```

The command leaves a consistent copy in `$BACKUP_DIR`, which the snapshot keeps instead of the live folder. The [PostgreSQL recipe](https://github.com/cthiriet/sitesolide/blob/main/docs/manifest.md#postgresql) gives both scripts.

## List the snapshots

From the project's folder, over the owner's SSH:

```bash
sitesolide backups
```

```text
=== backups of notes ===
TAKEN                 KIND          SIZE      ADDED     WHERE
2026-10-20 13:00 UTC  scheduled     12 MB     24 KB     server, offsite
2026-10-20 12:00 UTC  scheduled     12 MB     3.7 MB    server, offsite
```

## Restore a snapshot

The owner and the project's Admins restore.

1. Open the project's **Backups** section in the dashboard.
2. Click *Restore* on a snapshot, and unlock when asked.
3. Type the project's slug to confirm, then click *Restore*.

The server first saves the current data as a *Before restore* snapshot: restore that one to undo. If the app does not come back on the restored data, its previous data is put back. The dashboard and the portal are [restored by hand](https://github.com/cthiriet/sitesolide/blob/main/dashboard/src/backup/README.md#restoring-by-hand-when-the-dashboard-does-not-answer).

## Copy snapshots to a bucket

> [!WARNING]
> Until you configure a bucket, the snapshots stay on the server's own disk. A lost server takes its backups with it.

Create a bucket at an S3 provider, such as Hetzner Object Storage, Cloudflare R2 or Backblaze B2, with a key that may read, write, list and delete in it. Then create the settings file on the server, once:

```bash
ssh <user@host> 'sudo install -m 600 -o root -g root /dev/null /etc/sitesolide/dashboard-backup.env'
```

In the dashboard, open the **dashboard** site's **Secrets**, unlock, and fill `dashboard-backup.env`:

| Variable | Value |
|---|---|
| `BACKUP_S3_ENDPOINT` | The provider's HTTPS endpoint. |
| `BACKUP_S3_BUCKET` | The bucket's name. |
| `BACKUP_S3_REGION` | Its region, if the provider has one. |
| `BACKUP_S3_ACCESS_KEY_ID` | The key's ID. |
| `BACKUP_S3_SECRET_ACCESS_KEY` | The key's secret. |
| `BACKUP_ENCRYPTION_PASSPHRASE` | Click *Generate*. |

Store the passphrase in your password manager: without it, nobody reads the copy. The next hourly run copies the snapshots, encrypted on the server, and `sitesolide backups` lists them as `server, offsite`.

## Next steps

- [Monitoring](monitoring.md): hear when a snapshot fails.
- [Manifest reference](manifest.md): the `backup` key.

Full reference: https://github.com/cthiriet/sitesolide/blob/main/dashboard/src/backup/README.md
