# Backups and restore

## When to use this

Before risky changes, or after a problem. Reads need `backups:read`. Making and restoring backups needs `backups:write` (high risk, because a restore replaces data).

## Site backups

A backup is an encrypted package with the site's database, `.env` and commit. pfcloud also makes one before each deploy of a PHP site (unless `backup_before_deploy` is off) and on a schedule. Packages are stored off the server and stay encrypted. The API never returns the package itself: downloads need a person and their password in the dashboard.

```bash
# list (newest first, 25 a page)
curl -s -H "Authorization: Bearer $TOKEN" $MYFORGE/sites/SITE_ID/backups

# back up now (once per 5 minutes per site)
curl -s -X POST $MYFORGE/sites/SITE_ID/backups \
  -H "Authorization: Bearer $TOKEN" -H "Idempotency-Key: backup-shop-1"
```

A backup has `id`, `site`, `reason` (for example import, deploy, manual, scheduled, restore), `status`, `size`, `checksum`, `deployment`, `error` and `created_at`. Poll `GET /sites/{site}/backups/{backup}` until the status stops changing, and check `error`.

## Restore a database

```bash
curl -s -X POST $MYFORGE/sites/SITE_ID/backups/BACKUP_ID/restore \
  -H "Authorization: Bearer $TOKEN" -H "Idempotency-Key: restore-shop-1" -H "Content-Type: application/json" \
  -d '{"confirm":"shop.example.com"}'
```

`confirm` must be the site's domain, typed out. pfcloud takes a fresh backup first, checks the chosen package's checksum, puts the site in maintenance mode, empties the database and loads the backup. Only the database is restored, not files. It works for PHP sites pfcloud deploys, not for watch-only sites. `409` means busy, not stored yet, or not restorable.

> A restore throws away everything written to the database since that backup. An AI assistant must ask a person first, and name the backup and the data loss.

## Server backups

`POST /servers/{server}/backups` backs up every MySQL, MariaDB and PostgreSQL database on the server into one encrypted package. `GET /servers/{server}/backups` lists them. `PUT /servers/{server}/backup-schedule` with `{"schedule": "..."}` sets how often (limited by plan; scope `servers:write`).

## Limits

Plans decide which schedules you may use and how many backups are kept per site. Teams can use their own storage bucket (team settings).

## What can go wrong

| Problem | Fix |
| --- | --- |
| `409` back up too soon | Wait 5 minutes. |
| Backup has `error` | Read it. Typical cause: the database cannot be read with the `.env` settings. |
| Restore `409` | The backup is not stored yet, or the site is busy, moved or watch-only. |
