Docs/ Databases/ Backups & Restore

Backups & Restore

sh0 backs up database servers and volumes into compressed files, on demand or on a schedule, keeps them on the server or sends them to an external storage provider, and restores them in place.

What a backup contains

Each backup is one gzip-compressed file. Its content depends on the source:

SourceProduced by
PostgreSQL serverSQL dump: pg_dumpall for the whole server, or pg_dump when a single database is chosen
MySQL or MariaDB serverSQL dump: mysqldump --all-databases (mariadb-dump on recent MariaDB images), or one database
MongoDB servermongodump --archive of every database on the server
Redis servertar archive of the data directory /data (append-only and snapshot files)
App volumetar archive of the volume, or of a sub-path of it

The file is named after the backup ID: <id>.gz, or <id>.gz.enc when the backup is encrypted. There is no .dump, .archive or .rdb file. Local backups are stored in the backups/ folder of the sh0 data directory (/var/lib/sh0/backups/ on a standard Linux install):

Terminal
$ cd /var/lib/sh0/backups
$ ls -l 4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8.gz 9b5222c8-4c09-4805-8d96-4d80df2168c8.gz.enc | awk '{print $5, $9}'
1314 4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8.gz
1343 9b5222c8-4c09-4805-8d96-4d80df2168c8.gz.enc

$ zcat 4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8.gz | grep -m3 '^-- '
-- PostgreSQL database cluster dump
-- Drop databases (except postgres and template1)
-- Drop roles

Manual Backups

  • From a database server: its Overview tab has a Backup card with Backup Now. This backs up the whole server to local storage, unencrypted.
  • From the Backups page in the sidebar: Trigger Backup asks What do you want to back up?, the Destination, whether to Encrypt, and, for a database server, one database or all of them.

From the API, source_type is database_server, volume or database. The call answers 202 and the backup runs in the background; read it back to get its file:

Terminal
$ curl -s -X POST https://panel.example.com/api/v1/backups/trigger \
    -H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
    -d '{"source_type":"database_server","source_id":"3afde175-12d6-4614-94ca-e504737a7cc4","destination":"local","encrypt":false}' \
    -o /dev/null -w "%{http_code}\n"
202

$ curl -s https://panel.example.com/api/v1/backups/4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8 \
    -H "Authorization: Bearer $SH0_TOKEN"
{"data":{"created_at":"2026-10-02 02:46:04","created_by":"50159570-e8ad-46a8-8065-e6c9096edde6","database_name":null,"destination":"local","encrypted":false,"file_path":"/var/lib/sh0/backups/4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8.gz","file_size":1314,"id":"4efb5ef8-b61c-4b1f-b6dd-b546c1a01bb8","path":null,"project_id":null,"restore_error":null,"restore_status":"completed","restored_at":"2026-10-02 02:46:31","schedule_id":null,"source_id":"3afde175-12d6-4614-94ca-e504737a7cc4","source_type":"database_server","status":"completed"}}

Scheduled Backups

On the Backups page, Create Schedule takes the source, a five-field cron expression, the Destination and the retention. Schedules are evaluated in UTC. Run Now starts a scheduled backup immediately.

Terminal
$ curl -s -X POST https://panel.example.com/api/v1/backup-schedules \
    -H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
    -d '{"source_type":"database_server","source_id":"3afde175-12d6-4614-94ca-e504737a7cc4","cron":"0 3 * * *"}'
{"data":{"cron":"0 3 * * *","destination":"local","enabled":true,"id":"ed140ca5-1c47-4388-897d-52726489d3fa","last_run":null,"next_run":"2026-10-02 03:00:00","path":null,"retention":7,"service_id":null,"source_id":"3afde175-12d6-4614-94ca-e504737a7cc4","source_type":"database_server"}}

$ curl -s -X POST https://panel.example.com/api/v1/backup-schedules/ed140ca5-1c47-4388-897d-52726489d3fa/run \
    -H "Authorization: Bearer $SH0_TOKEN"
{"data":{"schedule_id":"ed140ca5-1c47-4388-897d-52726489d3fa","status":"started"}}
Note
Scheduled backups are never encrypted, and a schedule always backs up the whole server. For an encrypted copy or a single database, use a manual backup.

Retention

Retention is a number of backups, not a number of days. It defaults to 7; 0 keeps everything. After each successful scheduled run, sh0 deletes the oldest backups of that schedule beyond the limit, records and files alike. Manual backups are never pruned.

Storage Destinations

The default destination is local. External providers are added under Settings → Storage (the Add external storage provider link on the Backups page leads there). The provider types are:

  • S3-compatible: AWS S3, Cloudflare R2, DigitalOcean Spaces, Backblaze B2, Wasabi, MinIO, Hetzner Object Storage, or any S3 endpoint
  • SFTP, with a password or an SSH key
  • FTP, with optional TLS
  • Dropbox and Google Drive
  • Local path: another directory on the server, for example a mounted disk

In the API, destination accepts local, the name of a provider, or external:<provider-id>. Any other value is refused with 422 rather than silently stored locally.

Warning
A local backup sits on the same disk as the data it protects. It does not survive the loss of the server: send at least one schedule to an external provider.

Encryption

An encrypted backup (.gz.enc) is sealed with AES-256-GCM using the instance master key, stored as master.key and master.passphrase in the sh0 data directory. Only an instance holding that key can restore it: keep a copy of both files somewhere else than the server.

Restoring from a Backup

In the History of the Backups page, Restore replays a backup into the resource it came from, after you type its name to confirm. The current data is overwritten and sh0 does not take a backup first. The API call is POST /api/v1/backups/:id/restore; it answers 202 and the backup's restore_status then moves to completed or failed.

Restore from External Source restores a file that lives on a configured storage provider, for example a backup made by another sh0 instance.

Tip
Test a restore before you need one: restore a recent backup into a separate test server and check the data.

Download and Delete

Download in the history, or GET /api/v1/backups/:id/download, returns the file as stored (application/gzip, or binary when encrypted). Deleting a backup removes its record and its file.