Backups
Sirio backs up each site with restic: its Docker volumes, the host folders its containers mount, and its databases, dumped with their own tools. Snapshots are encrypted with a password only you hold, deduplicated (a nightly backup only stores what changed) and pruned according to a retention policy you choose.
restic runs as a one-off container, started by Sirio through the Docker socket: no privileged
helper and no exception to the service’s sandbox are needed, and Sirio itself never reads the
data. The image is pinned by digest (SIRIO_RESTIC_IMAGE overrides it).
The snapshots are plain restic snapshots. Everything Sirio does can be undone or inspected with restic alone, from any machine, even if the server and Sirio are gone: see Recovery kit.
Destinations
Section titled “Destinations”A destination is a restic repository. Several plans, and several sites, can share one.
| Type | Repository | Credentials |
|---|---|---|
| Local folder | a path on this server | none |
| SFTP | sftp:user@host:/path |
an SSH key, generated by Sirio or pasted |
| Amazon S3 | s3:s3.<region>.amazonaws.com/<bucket>/<folder> |
access key and secret key |
| S3-compatible | s3:<endpoint>/<bucket>/<folder> |
access key and secret key |
| Backblaze B2 | s3:https://s3.<region>.backblazeb2.com/<bucket>/<folder> |
key ID and application key |
| Google Cloud Storage | gs:<bucket>:/<folder> |
a service account’s JSON key |
- Local folder. Docker creates it if it’s missing. On the same disk as the data it protects from mistakes, not from a disk failure: use a second disk, a mounted NAS, or another destination as well.
- SFTP. Sirio connects with its own key and only to the host key you confirmed: when you
create the destination, Sirio reads the server’s key and shows its fingerprint, which you
compare with the server’s own (
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub). A server presenting another key later is refused, never trusted on first use: backups to it fail before anything is dumped or stopped, and the run says so. If the server was reinstalled, edit the destination and confirm its new fingerprint. If Sirio generates the key, add the public key it shows to~/.ssh/authorized_keysof the user on the server. Avoidbackupas the user name on Debian-based servers: it’s a system account there, whose home isn’t where you’d put the key. - S3-compatible. MinIO, Wasabi, Cloudflare R2, Hetzner, Scaleway, OVH and the like: give the endpoint as your provider documents it, and the region if it asks for one.
- Backblaze B2 goes through B2’s S3-compatible API, which restic’s documentation recommends
over the native
b2backend. The endpoint is in the bucket’s details (s3.<region>.backblazeb2.com). - Google Cloud Storage. The service account needs Storage Object Admin on the bucket. The project ID is taken from the key when left empty.
Give each destination credentials limited to its bucket. They’re stored encrypted with Sirio’s secret key, like Git tokens, and the API never returns them: editing a destination leaves a credential field empty to keep the stored one.
The repository password
Section titled “The repository password”Every snapshot is encrypted with the repository’s password. When you create a destination Sirio generates one and shows it once: save it outside the server, in a password manager. Without it nobody can read the backups — Sirio can’t recover it for you, and if the server is lost, the copy inside Sirio’s database is lost with it.
To use a repository that already exists (made by hand, or by another Sirio), choose Use the password of an existing restic repository instead.
Check opens the repository with its password and creates it if it doesn’t exist yet — on S3-compatible storage, B2 and GCS the bucket too, if the key may create one. The first backup does the same on its own. restic retries a failing request for about fifteen minutes, which helps a backup ride out a network blip; checks, snapshot listings and the start of every run give up after three attempts instead and report restic’s error, so wrong credentials show up in seconds.
A plan backs up one site to one destination, on a schedule, keeping what the retention says.
What gets backed up
Section titled “What gets backed up”The containers assigned to the site (in Containers, or claimed by a deploy of a pipeline linked to the site). For each one the plan decides how:
| Mode | What happens |
|---|---|
| Database dump | the database is exported with its own tool while it runs. Consistent, and its data files are left out. The default for a recognized database. |
| Stop while copying | the container is stopped while its files are copied, then started again. Consistent, with a short downtime. |
| Copy while running | the files are copied while the container runs. The default for everything else: fine for uploads and static files, not for a database’s own files. |
| Leave out | nothing of this container is backed up. |
What’s copied: Docker volumes (named and anonymous) and bind-mounted host folders. Never: the
Docker socket, the host’s system paths (/proc, /sys, /dev, /run, /etc/hosts and the
like), tmpfs mounts. A volume shared by two containers is copied once.
Each volume and host folder can be left out on its own: untick it in the plan’s container table.
The choice belongs to the plan, not to the container, since a volume shared by two containers is
copied once — leaving it out from one leaves it out from both. A host folder left out that sits
inside one still copied (say /srv/shop/media/thumbs under /srv/shop/media) is skipped by
restic. A container in Stop while copying mode whose mounts are all left out isn’t stopped.
A database in dump mode that isn’t running can’t be dumped: its files are at rest, so they’re copied instead, which is safe.
Databases
Section titled “Databases”Sirio recognizes the official images (and Bitnami’s) of MySQL, MariaDB, Percona and PostgreSQL (with PostGIS, TimescaleDB, pgvector), by image name: applications connecting to a database carry its variables too, so the environment alone would mislead.
The dump runs inside the database’s container (docker exec), with a fixed script that reads
the credentials from the container’s own environment — the variables you set up the image with.
No password passes through Sirio, and none ends up on a command line.
- MySQL / MariaDB: as root when
MYSQL_ROOT_PASSWORD/MARIADB_ROOT_PASSWORD(or their_FILEvariants, or the empty-password ones) are set, everything with--all-databases; otherwise asMYSQL_USERonMYSQL_DATABASEonly.mariadb-dumpwhen present (MariaDB 11 no longer hasmysqldump). The plan editor warns when no credentials are found. - PostgreSQL:
pg_dumpall --clean --if-existsasPOSTGRES_USER(defaultpostgres), roles included: the dump drops each database before recreating it, so it restores over a live cluster.
The dump is written to a temporary folder under SIRIO_DATA_DIR/backups, included in the
snapshot and deleted right after: it needs as much free space there as the dump’s size. A dump
that fails leaves no file behind, so a truncated dump never looks like a good one; the run is
marked failed, but the rest of the site is still backed up.
Schedule
Section titled “Schedule”Every hour at a minute, every day or every week at a time, or a cron expression (five fields,
plus @hourly, @daily, @weekly…), in the server’s time zone. A backup missed while Sirio was down runs once when it
starts again. A plan can also be run only by hand.
A backup and a deploy of the same site never overlap: whichever starts second waits, and its log says so. Backups run one at a time.
From a pipeline
Section titled “From a pipeline”A pipeline linked to a site can back it up as one of its steps: add a Backup step, pick one of the site’s plans, and put it before the deploy steps. The step waits for the snapshot (its timeout bounds the backup too) and fails the run if the backup fails, so a deploy never goes ahead without its safety net. The backup shows up in the plan’s history like any other, started by “pipeline”.
Retention
Section titled “Retention”After each backup, restic’s forget --prune removes this plan’s snapshots beyond the counts you
set — the last N, one per day for N days, one per week, one per month — and frees their space.
With every count at zero nothing is ever removed.
Retention only touches the plan’s own snapshots (tag plan:<id>, taken from this host), so plans
sharing a destination don’t prune each other.
Recovery kit
Section titled “Recovery kit”Recovery kit (in Destinations, after confirming your own password) shows what’s needed to reach the backups with restic alone: the repository, its password, the variables naming the storage credentials (those stay yours to fill in), and the commands to list and restore.
export RESTIC_REPOSITORY='s3:https://s3.eu-central-003.backblazeb2.com/my-backups/sirio'export RESTIC_PASSWORD='…'export AWS_ACCESS_KEY_ID='…'export AWS_SECRET_ACCESS_KEY='…'
restic snapshots --tag site:shoprestic restore latest --host web1 --tag site:shop --target ./restoreInside a snapshot, backup/<site>/ holds volumes/<volume> (Docker volumes),
binds/<host path> (host folders) and databases/<container>.sql (dumps). To restore a
database, feed the dump to the database’s client: mysql < shop-db.sql or
psql -f shop-db.sql.
Every snapshot carries the tags sirio, site:<site> and plan:<plan id>, and the server’s
host name.
Snapshots and restores
Section titled “Snapshots and restores”A plan’s page (its name in Backups) lists its snapshots — only its own, even when the destination is shared — and lets you walk any of them folder by folder. From there:
- Download any file as it is, or any folder as a
.zip, streamed out of the repository. - Restore a volume: over the site’s own volume, or into a new one (the current data stays untouched; mount the new volume where you need it). Replacing stops the containers using the volume, makes its content exactly the snapshot’s — files added since are deleted — and starts them again.
- Restore a host folder, in place only, among the folders the site’s containers still mount: same stop, replace, start.
- Restore a database from its dump into one of the site’s running database containers. By
default the site’s other containers are stopped meanwhile, so no application writes halfway and
PostgreSQL can drop the databases it recreates. MySQL/MariaDB stop at the first error; for
PostgreSQL, the two errors about the role the restore runs as are expected, any other fails the
restore. Dumps taken before Sirio added
--cleanrestore cleanly only into an empty database.
Restores run in the same queue as backups, wait for any backup or deploy of the site, and have a page with their live log. Every target is checked twice, when asked and when the restore starts: a restore can only write into what belongs to the plan’s site, never elsewhere on the host.
Permissions
Section titled “Permissions”- Backups (global permission): destinations, and the plans of every site. Its exceptions apply everywhere, like the other global permissions.
- On a site: Backups (seeing the site’s plans, runs and snapshots, and the names in them), Edit (creating, editing and deleting its plans, choosing among the existing destinations), Run (running and canceling a backup by hand) and Restore and download (reading the backed-up data back: downloading files, restoring them over the site’s). Restore is its own action because it can replace live data.
Someone with only site permissions sees the destinations’ names, not where they are.
Where things are
Section titled “Where things are”- Run logs:
SIRIO_DATA_DIR/backups/logs, kept for the lastSIRIO_RUN_RETENTIONruns of each plan. - restic’s cache: the Docker volume
sirio-restic-cache, so a run doesn’t download the repository’s indexes again. - While a run lasts: dumps under
backups/staging, credential files underbackups/tmp(0600, mounted read-only into restic’s container, deleted at the end).
A Sirio restart in the middle of a backup or a restore marks it failed, removes the restic container it left behind and starts again the containers it had stopped.