# Backups and restore

> Back up a Frame server's database, secrets.json and media every night, copy them off the server, and restore them on a new machine.

Source: https://crow.sg/apps/frame/docs/backups/

Back up three things, and copy them off the server:

| What | Where (Linux installer) | Why |
|---|---|---|
| The database, made with `backup` | written wherever you ask | Accounts, friendships, moments and messages. |
| `secrets.json` | `/var/lib/frame/secrets.json` | Signing keys and the key that encrypts R2/S3 media. Lose it and that media can't be read. |
| `media/` (local storage only) | `/var/lib/frame/media/` | The photos and videos. |

The database backup is consistent and safe to take while Frame runs. With [R2](https://crow.sg/apps/frame/docs/storage/#r2), the media is already in the bucket, so the database and `secrets.json` are what matter; for extra safety, mirror the bucket elsewhere with `rclone sync`.

## Linux installer (systemd)

```sh
sudo frame-admin backup /root/frame-$(date +%F).db       # consistent, safe while running
sudo tar -C /var/lib/frame -czf /root/frame-files-$(date +%F).tgz secrets.json media
```

Copy both files off the server, for example with `scp` to your computer, or with `restic` or `rclone` to another provider. Run it nightly with cron (`sudo crontab -e`):

```text title="crontab"
15 3 * * * /usr/local/bin/frame-admin backup /root/frame-$(date +\%F).db && tar -C /var/lib/frame -czf /root/frame-files-$(date +\%F).tgz secrets.json media
```

Old backups pile up, so delete them now and then, or let your backup tool rotate them.

## Docker

```sh
docker compose exec frame frame-server backup /data/frame-backup.db
docker compose cp frame:/data/frame-backup.db ./frame-$(date +%F).db
docker run --rm -v frame_frame-data:/data:ro -v "$PWD":/backup alpine \
  tar -C /data -czf /backup/frame-files-$(date +%F).tgz secrets.json media
```

With the Caddy setup, add `-f docker-compose.caddy.yml` after `docker compose`.

## From the admin panel

**Maintenance** in the admin panel downloads a consistent copy of the database. It asks for your password again, and each download link works once, for two minutes. It doesn't include `secrets.json` or the media, so it doesn't replace the server-side backup above.

## Restore

On the Linux installer (on a new machine, run `install.sh` first and copy over `/etc/frame/frame.conf`):

```sh
sudo systemctl start frame && sudo systemctl stop frame   # makes sure the data dir exists
sudo tar -C /var/lib/frame -xzf frame-files-2026-11-07.tgz
sudo cp frame-2026-11-07.db /var/lib/frame/frame.db
sudo rm -f /var/lib/frame/frame.db-wal /var/lib/frame/frame.db-shm
# hand everything back to the service's user (the owner of the data dir itself):
sudo chown -R --reference=/var/lib/private/frame /var/lib/private/frame
sudo systemctl start frame
```

Try a restore once, on a spare machine or a throwaway VPS, before you need it.

> [!NOTE] Backups keep deleted content
> Earlier backups still contain moments and messages people have since deleted. Rotate old backups, and keep them as private as the server.
