Skip to content

Powered by Grav

Backups on the static filesystem

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.

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 carries files/private as 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:

TXT
/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-files copies 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:group is captured first and applied to the store directory and (with chown -h) to the symlink itself.
  • Serialised. One account migrates at a time, guarded by a real flock on /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.sh honours 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.sh purges a leaked one as soon as its owner PID is gone, and /run clears 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

BASH
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 backupsbackup-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.

  • 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/files ends up on a separate filesystem in the first place.

© 2026 BOA Documentation. All rights reserved.