Backups and restores¶
Every instance lives in its own btrfs subvolume. A backup is a read-only btrfs snapshot of that subvolume: it takes milliseconds and costs almost no space until files change. A snapshot of a running server is crash-consistent, like a power cut; the snapshots taken before a change are taken with the server stopped.
dzo creates the instance directory as a subvolume on its first
dzo instance render, as the unprivileged service user, and everything below
works without root. paths.instances and paths.snapshots must be on the
same btrfs filesystem; on any other filesystem an instance runs, but
dzo backup refuses it and says why. dzo setup reports whether the
filesystem is mounted with user_subvol_rm_allowed, which makes deleting
snapshots instant; without it dzo clears the read-only flag and removes the tree
file by file, which is slower but needs no privileges.
Snapshots live in <paths.snapshots>/<instance>/<time>-<reason>, and
index.json next to them records each one (reason, time, state, pinned). dzo
only ever acts on snapshots in that index.
When snapshots are taken¶
Reason |
When |
Default |
|---|---|---|
|
before |
on |
|
before new mod versions are applied, including automatic updates |
on |
|
before a new pristine mission is applied |
on |
|
before site repository changes are applied |
on |
|
before every start, including crash restarts |
off |
|
periodically while running ( |
off |
|
|
– |
|
before |
always |
If a snapshot fails, the change is not applied, the server starts unchanged and you are alerted.
Retention¶
The backup policy is the backup: block in the site repository. Put it in
instances/<name>/instance.yaml for one instance, or under defaults: in
site.yaml for every instance that does not set its own (see
Configuration). A key the instance leaves unset is taken from the
defaults, so the instance only needs the values that differ (keep_by_reason
is taken as a whole, not per reason). Keys set in neither place get the
built-in values. Commit the change
and run dzo site pull on the host; dzo site validate checks the file.
# instances/<name>/instance.yaml
backup:
before: [update, mod_update, mission_update, config_change] # default; also: render; [] = none
schedule: [] # default: none; times of day, like restarts.schedule
keep: 20 # default
keep_by_reason: {mod_update: 8, update: 10, scheduled: 12} # default: no per-reason limit
max_age: 30d # default: no age limit
min_keep: 3 # default; never fewer, regardless of age
min_free_bytes: 50GiB # default: none; prune the oldest first to keep this free
schedule takes the same entries as restarts.schedule: HH:MM, or
Mon,Thu HH:MM for some weekdays only, and *:MM for every hour at that
minute (hourly is short for *:00). Each entry becomes a time of the
dzo-backup-<name>.timer that dzo instance apply writes. For example, a
snapshot every six hours and an extra one on Saturday evening:
backup:
schedule: ["00:00", "06:00", "12:00", "18:00", "Sat 22:30"]
keep_by_reason: {scheduled: 12} # keep the last 12 of these
or one every hour, with the last 24 kept:
backup:
schedule: [hourly] # the same as ["*:00"]
keep_by_reason: {scheduled: 24}
The same block as a default for all instances:
# site.yaml
defaults:
backup:
keep: 20
max_age: 30d
Old snapshots are pruned after each successful snapshot, by the daily
dzo-backup-prune.timer, and on demand:
dzo backup prune [<instance>] --dry-run
dzo backup pin <instance> <id> # never pruned automatically
dzo backup unpin <instance> <id>
dzo only deletes snapshots it has recorded. Unknown directories in the snapshot
folder are reported, not deleted, unless you pass --delete-orphans (or
--adopt-orphans to enter them into the index). keep is the total per
instance, pinned snapshots included; the newest snapshot and min_keep
snapshots are never removed, whatever the other limits say. A snapshot that is
being restored from is protected for the duration of the restore.
Listing and comparing¶
dzo backup create <instance> [--reason manual]
dzo backup list [<instance>]
dzo backup diff <instance> <id> [<id2>|live]
Snapshots are plain read-only directories, so you can also browse them directly or download files in the web interface.
Restoring¶
Full restore:
dzo restore deerisle <id> # stops the instance if it runs, starts it again afterwards
The instance is stopped, a safety snapshot is taken, the current data is moved aside (and kept as a snapshot), the backup becomes the live instance, and the server starts.
Restore only part of an instance, for example the world or one mod’s data (the
world is below storage/<map>):
dzo restore deerisle <id> --path storage/empty.deerisle/storage_1
A restore takes a pre_restore snapshot first, so restoring the wrong snapshot
can be undone, and the data it replaces is kept as a snapshot of reason
replaced. If the restore fails, the instance is left as it was.
What is not there yet: the snapshot before a server or mod update (those changes are not automated yet), and the backup of the database, which does not exist yet.
Offsite copies¶
Snapshots stay on the same filesystem, so they do not protect against disk
loss. Add a post_backup hook that copies new snapshots elsewhere, for
example with restic or borg; example hooks are shipped in
/usr/share/doc/dzo/examples/backup-hooks/.