Native files symlinking

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:

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/, and — 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.

The subsystem acts at three 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. The gated nightly batch: converts pre-existing plain-directory sites and archives deleted-site orphan stores — 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.

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. And 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.

Two kill-switch tiers disable native symlinking without a code change: box-wide /data/conf/disable_native_files_symlink.cnf, or 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 everywhere except cloning.

Pages in this topic

  • Overview — the store layout, the event table (install, clone, migrate/rename, name reuse, nightly), the privilege model and account scope, the safety properties, kill-switches, and deliberate file sharing.
  • Tools referenceautosymlink (global DRY / report / live / batch / --batch-if-clean modes and the narrow single-site mode), the fix-drupal-site-symlinks.sh sudo wrapper, the read-only symlinkinfo history query, verify commands, logs, and deployment.
  • Nightly automationupdatesymlinks --auto-fix and --orphan-report, the hourly-night cron line, the task-queue pause and its grace, the heavy-task skip and per-night stamp, and --debug.
  • Configuration_AUTOSYMLINK_NIGHTLY, _ORPHAN_FILES_REPORT, _AUTOSYMLINK_PAUSE_GRACE, how Barracuda persists them to /root/.barracuda.cnf, the defaults-by-system-class story, and the full control-file catalogue.
  • Orphans & archiving — the strict alias+vhost orphan test, the .archived/<UTC-stamp>/ layout and why it lives under static/files, reuse-time archiving, the accumulation alert and its threshold, and operator-only pruning.
  • Backups on the static filesystem — the nightly relocation of backups/backup-exports onto an attached-storage account's static filesystem, the different-device gate, and its kill-switches.

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.