Backups and destinations
Scheduled backup jobs, what they can contain, and shipping their output to your own S3-compatible bucket.
This page is the self-hosting angle on backups: shipping your scheduled jobs to your own external storage, and how the resulting archives restore onto an instance. For creating a job in the first place - its name, scope (whole workspace or a single project), cron schedule, and retention window - see Backups in the admin guide; that flow is identical whether you're self-hosted or on cloud. This page picks up from the job form's destination field.
Every job (Admin → Backup) can ship its output to the instance's own storage (the default) or to your own S3 bucket - any S3-compatible service: AWS S3, Cloudflare R2, Backblaze B2, MinIO, Hetzner Object Storage. Each job also chooses what it ships - the content scope below.
Content scopes
| Scope | What lands at the destination |
|---|---|
| Database + files | The classic combined ZIP: every table as NDJSON plus every storage object. Restorable as-is through the setup wizard. |
| Database only | The same ZIP without the storage objects - small, fast, ideal for frequent schedules. Pair it with a "Files only" job so the objects are covered too. |
| Files only (S3 mirror) | No archive. Every owned storage object (attachments, avatars) is mirrored to <prefix>/storage/<key> in your bucket - incremental by key + size, nothing is ever deleted on the remote. External destinations only. |
A sensible offsite setup is two jobs against the same bucket: a daily "Database only" archive plus a daily "Files only" mirror. The mirror only transfers new/changed objects, so daily runs stay cheap even with a large attachment history.
Configuring an external destination
In the job form, pick External S3 bucket and fill in endpoint
(empty for AWS), region, bucket, access key and secret. Test
connection verifies the credentials against the bucket before you
save. The secret is encrypted at rest with the instance's key envelope
and is never returned by the API - edit forms show ***, which keeps
the stored secret unless you type a new one.

Restoring
- An archive in your bucket restores like any other backup: download the ZIP and feed it to the setup wizard (Restore from a backup) - see First-run setup.
- A "Database only" archive plus the mirrored
storage/prefix restore together: import the archive first, then copy the mirrored objects into the instance's storage under their original keys (the keys under<prefix>/storage/ARE the original keys).
Run results
Every run lands in the run history with the destination path
(s3://bucket/...) and, for mirrors, the new/unchanged object counts.
A mirror run fails (and alerts like any failed backup) when any object
could not be transferred.
Notes
- Retention (
retentionDays) applies to the internal storage cleanup; objects in YOUR bucket are yours - manage lifecycle rules there. - The mirror only ships workspace content (the same owned-keys filter the exports use); internal backup archives never leave the instance through a mirror.
Troubleshooting
Test connection fails when I set up an external destination. Cause: wrong endpoint/region for a non-AWS provider, a bucket that doesn't exist yet, or credentials without write access. Fix: for a non-AWS S3-compatible service, the endpoint field is required (only AWS leaves it empty); confirm the bucket exists and the access key can write to it.
A scheduled run shows as failed in the run history. Cause: for a mirror job, at least one object couldn't be transferred - usually a transient network error or a credential that expired mid-run. Fix: open the run's details for the specific object(s) that failed, then re-run the job; it only needs to (re-)transfer what's missing or changed.
Restoring a "Database only" archive leaves attachments broken.
Cause: a database-only archive intentionally excludes storage objects -
you need to also restore the paired "Files only" mirror from the same
bucket.
Fix: after importing the archive, copy the mirrored objects from
<prefix>/storage/ into the instance's storage under their original keys.