Backups on the static filesystem
A backup of a native-symlinked site can be large: files-carrying
Backup-task archives always follow the files/private symlinks (dereference
the store), and boxes opted into dereference_backups.conf
(below) do the same for implicit backups, so the tarball holds the full file
data. Those backups land in
/data/disk/<account>/backups on the root partition — a disk-full risk for a
big account whose files live on attached storage.
Backup modes and symlink handling
The panel Backup task modes that include site files (Site files without
any DB, Site files with classic mysqldump DB) always dereference the
files/private symlinks, so the archive is self-contained and restorable on
any box — an operator who asked for a files backup gets the real content,
never a bag of bare symlinks.
Every other backup preserves symlinks by default: the DB-only backup
mode and all implicit backups — the safety copies taken before Restore
and Delete, the pre-clone/pre-migrate source backups, and nightly queued
backups. That is smaller and faster, and safe for those flows because the
file content still lives in the per-account store (clone, migrate and restore
all re-home it via the narrow conversion); a site with
plain real dirs is archived in full either way, since not following links
never skips real directories. Two /data/conf/ control files adjust this:
dereference_backups.conf— opt those backups back into dereferencing (self-contained safety copies, at the cost of duplicating store data into each one). A DB-only archive taken this way carriesfiles/privateas skeleton dirs, which the Restore task detects — it keeps the existing store instead of emptying it.force_symlinks.conf— the legacy hard-preserve switch: redundant now that preserving is the default, still honoured, and it beats the opt-out above. It can be removed at leisure; no new system needs it.
Note that a preserved-symlink archive is not self-contained — it is not a disaster-recovery artifact on its own.
For such accounts the nightly per-account maintenance relocates backups and
backup-exports onto the static filesystem and replaces them with symlinks:
/data/disk/<account>/backups -> /data/disk/<account>/static/files/.backups
/data/disk/<account>/backup-exports -> /data/disk/<account>/static/files/.backup-exports
The Ægir paths are unchanged — backup_path and aegir_backup_export_path
keep working through the symlinks (the same nightly run re-asserts the export
path to <account>/backup-exports in its hostmaster settings block). Both
targets live under static/files, so they share one filesystem and the
backup-download hardlinks between backups and backup-exports keep
working (hardlinks cannot cross filesystems). The leading-dot names are skipped
by the site/orphan scan, like static/files/.archived — they can never be
mistaken for site stores. The nightly local purges glob through the symlinks
unchanged (-mtime +_PURGE_BACKUPS on backups, -mtime +_PURGE_TMP on
backup-exports).
When it acts
The relocation is gated on static/files resolving (symlinks followed) to a
different device than the account root — a stat -c '%d' comparison. On a
default single-filesystem box static/files sits on the root device, there is
nowhere better to put backups, and the run is a deliberate no-op. It only
acts once an account's static/files is on attached storage — typically after a
migratefs relocation.
A backup directory that does not exist yet is left alone: Ægir creates it on first use and a later nightly run relocates it. Once both paths are symlinks the nightly pass just re-verifies the targets and creates a missing store directory — no lock, no queue pause (the steady-state fast path). A symlink pointing at an unexpected target is never touched; it is logged for operator review.
One-time migration safety
Moving existing backups is the only destructive-looking step, and it is built to never lose one:
- Incremental, low-space.
rsync --remove-source-filescopies file by file and removes each source only after it is copied — peak extra space on the tight root filesystem is roughly one file, not a full second copy. - Fail-open. On any failure the real directory stays in place and no symlink is created; the directory is replaced only after it emptied cleanly. A non-empty leftover is logged and left for review. A backup is never deleted.
- Ownership preserved. The source's
owner:groupis captured first and applied to the store directory and (withchown -h) to the symlink itself. - Serialised. One account migrates at a time, guarded by a real
flockon/run/.boa_backups_relocate.flock— the kernel releases it if the process dies, so there is no stale-lock guessing.
Task-queue interlock
So that no backup task writes into a directory being moved, the migration holds
the Ægir task queue with the dedicated stop file /run/boa_queue_stop.pid
(its own PID; an existing hold is respected, never overwritten):
runner.shhonours it twice — the parent exits immediately, and each per-account child dispatch skips too, so a pause set after a runner started still holds every dispatch.- Any already-running task is drained first (bounded wait, ~60 s); if one is still active the relocation defers to the next night rather than move a backup mid-write, and the interlock is re-checked once more right before each move.
- The mechanism is self-healing by design: the run removes the stop file only if
it created it and still owns it (recorded PID verified),
clear.shpurges a leaked one as soon as its owner PID is gone, and/runclears on reboot — so it cannot freeze the queue. The same generic stop file is used by the nightly symlink automation; the queue side is described on Task queue.
Verification
readlink /data/disk/<account>/backups
readlink /data/disk/<account>/backup-exports
On a relocated account both print the static/files/.backups /
.backup-exports targets; on a single-filesystem box both remain real
directories. The per-account nightly log records the outcome with
backups-on-static: lines — relocated <dir> -> <target> (static filesystem)
on success, move failed / not empty after move / could not symlink when a
step was skipped fail-open (the data is then still in place, or safe in the
store awaiting a manual relink).
Kill-switches
On by default where the filesystem gate applies; disable without a code change:
| Control file | Scope |
|---|---|
/data/conf/disable_backups_on_static_fs.cnf |
box-wide — all accounts on this host |
/data/disk/<account>/static/control/no_backups_on_static_fs.info |
one Octopus account |
While present the nightly pass returns before doing anything — it stops future relocation but never moves already-relocated data back; undoing a relocation is a manual operation. The full control-file catalogue is on the Configuration leaf.
Known caveat
Pre-existing backups↔backup-exports hardlink pairs are de-linked by the
one-time move — the two directories migrate in separate rsync runs (no -H),
so old tarballs briefly cost double space, on the static filesystem (the target),
not the tight root one. The duplication ages out with the normal purge sweeps —
_PURGE_TMP clears the backup-exports copy, _PURGE_BACKUPS the backups
copy. Backups taken after relocation hardlink normally,
since both directories are then co-located on one filesystem.
Related pages
- Overview & safety model — the symlink layout whose dereferenced backups this page is about.
- Configuration — every control file and cnf variable of the symlinking subsystem.
- Backups overview — per-account backup directories may be symlinks; what that means for off-site include/exclude sets.
- Task queue — how the runner dispatches tasks and honours queue pauses.
- migratefs storage relocation
— how
static/filesends up on a separate filesystem in the first place.