Skip to content

Powered by Grav

Native files symlinking

Native files symlinking

How BOA keeps each site's writable files and private directories in the account's static store, symlinked back into the platform, with a warn-not-fail safety model.

BOA keeps each site's writable files and private directories outside the platform docroot, in the owning Octopus account's static store, and links them back into the site:

TXT
sites/<url>/files    ->  /data/disk/<account>/static/files/<url>/files
sites/<url>/private  ->  /data/disk/<account>/static/files/<url>/private

Only a symlink lives in the platform, so:

  • a site's uploads survive platform rebuilds and code redeploys untouched;
  • every site's data has one per-account home under static/files/;
  • because the store is a single directory tree, the whole store can be relocated onto attached /mnt storage with migratefs while the per-site links keep working through it.

Where the subsystem acts

The subsystem acts at four points:

  • New-site install: Provision's post-install hook has the just-created real files/private moved into the store and symlinked, before the first verify writes the vhost.
  • Clone and Migrate/rename: after the target verify, the new site is re-homed into its own store with --force-unshare, which breaks the inherited link into the source name's data even when a sharing control file exists — a fresh clone or renamed site never opted into the share.
  • Delete, and the old name after a rename: the task itself asks the root wrapper to set the store aside into static/files/.archived/ before the alias goes (DELETE/STORE/ARCHIVED, RENAME/STORE/ARCHIVED) — never a deletion, never a store a live site still reads, and independent of the nightly opt-in.
  • The gated nightly batch: converts pre-existing plain-directory sites and archives any leftover orphan store the tasks did not take — opt-in everywhere except the omega8.cc-hosted (.aegir.cc) fleet, where it is seeded on by default.

Existing unconverted sites are deliberately not touched by a normal install or verify; only the nightly auto-fix or a manual run moves data at a time an operator chose.

Safety model

The safety model is uniform across all of it:

  • Every path is warn-not-fail: low disk, a missing tool, or any non-zero step logs a warning and the task still succeeds — a conversion falls back to plain real directories, and a clone unshare that cannot complete leaves a still-shared link flagged with an operator-actionable [ALERT] for a manual re-run once space allows; never a dangling link, never an aborted install or clone.
  • Moves are crash-safe and rc-checked: a symlink is never created over a failed or partial move, and an interrupted run is self-healed on the next pass.
  • Stale and orphaned stores are archived, never deleted — moved into static/files/.archived/<stamp>/, with pruning left strictly to the operator: boa archived list shows the pile, boa archived prune removes what you name (see Configuration).
  • Root-managed conversion: because the store is root-managed while Provision tasks run unprivileged, the conversion always runs as root through a hardened NOPASSWD sudo wrapper (/usr/local/bin/fix-drupal-site-symlinks.sh) that fail-closed validates its arguments and can reach only the narrow single-site apply — never the global batch/live modes.

Scope is /data/disk/<account> Octopus accounts only; the master hostmaster account keeps plain directories.

Kill-switches and file sharing

Two kill-switch tiers disable native symlinking without a code change:

  • box-wide /data/conf/disable_native_files_symlink.cnf;
  • per-account /data/disk/<account>/static/control/no_native_files_symlink.info.

Deliberate cross-site file sharing remains possible via static/control/share.files.<site>.info, honoured by every task except the copying ones (clone, migrate/rename, the Backdrop upgrades), which always give the new site its own copy.

Cross-cutting

  • The Clone and Migrate tasks whose logs now carry the [native-symlink] success line (and, on low disk, an operator-actionable [ALERT]) are in Migration & cloning; relocating the account-level static/files store itself onto attached storage is migratefs.
  • The nightly window the automation shares — cadence, heavy-task skips, and the other night workers — is in the monitor stack; the task-queue pause mechanism it holds is the task queue.
  • The sudoers entry for the wrapper and the AppArmor mrix exec edges added to every PHP profile are in Security & isolation (AppArmor).
  • The backup subsystem whose per-account backups directories the nightly relocation moves is Backups.
  • The _AUTOSYMLINK_* / _ORPHAN_FILES_REPORT variables are catalogued in the host control-file reference and the Reference appendix.

Native files symlinking — overview & safety model

The store layout and event model for native files symlinking, plus the privilege and account scope, the warn-not-fail safety properties, kill-switches and file sharing.

Files-symlink tools

The three executables behind the subsystem: the autosymlink worker and its modes, the hardened sudo wrapper, and the read-only symlinkinfo history query, with logs.

Nightly automation — updatesymlinks

The updatesymlinks scheduler around autosymlink: the auto-fix and orphan-report sub-modes, the nightly cron line, the task-queue pause and grace, and the heavy-task skips.

Files-symlinking configuration reference

The three barracuda.cnf variables that govern the nightly automation, how Barracuda persists them, the defaults by system class, and the full kill-switch control-file catalogue.

Orphan stores & the .archived archive

How a site's store is set aside into a dated .archived directory by the Delete task itself, by a rename and at name reuse, how the nightly sweep's strict alias-and-vhost test catches what is left, and why nothing is ever deleted and pruning is yours.

Backups on the static filesystem

The nightly relocation of an account's backups and backup-exports onto its static filesystem, gated on a different-device test, with its no-op default and kill-switches.

Why a dry run is NOT CLEAN — the EXPLAINED section

Why one anomaly marks a whole dry run NOT CLEAN and blocks batch conversion, and how the read-only EXPLAINED section re-probes each finding with its diagnosis and fix.

© 2026 BOA Documentation. All rights reserved.