Why a dry run is NOT CLEAN — the [EXPLAINED] section
A dry run ends CLEAN (batch and live allowed) or NOT CLEAN. A single
anomaly on a single site anywhere in the sweep marks the whole run NOT CLEAN and
blocks batch/live for every account on the box — so a nightly --batch-if-clean
exits 10 and converts nothing, every night, until the cause is resolved.
The verdict line itself names neither the site nor the reason. So a NOT CLEAN run
ends with an [EXPLAINED] section: every finding behind the verdict is
re-checked against the filesystem right then, and printed with what was seen,
what was probed, the diagnosis, and the fix.
[EXPLAINED] 1 finding made this run NOT CLEAN. It is re-checked below against the
[EXPLAINED] filesystem as it is right now, and nothing here was changed.
[EXPLAINED] Batch and live mode stay blocked until every finding is resolved and a fresh
[EXPLAINED] dry run reports CLEAN.
------------------------------------------------------------------
[EXPLAINED] (1/1) SITE_PATH_DIR_ABSENT -- site shop.example.com in account o3
[EXPLAINED] Seen: the Drush alias names a site_path that is not on disk, so the site was
[EXPLAINED] skipped and the run was marked NOT CLEAN.
[EXPLAINED] Alias : /data/disk/o3/.drush/shop.example.com.alias.drushrc.php (modified …)
[EXPLAINED] site_path : /data/disk/o3/distro/001/drupal-7.105.1-stage/sites/shop.example.com -- MISSING
[EXPLAINED] nginx vhost: present, real docroot (nginx still serves this name)
[EXPLAINED] Files store: none at /data/disk/o3/static/files/shop.example.com (no files data at stake)
[EXPLAINED] Platform : /data/disk/o3/distro/001/drupal-7.105.1-stage (present, sites/ entries: 6)
[EXPLAINED] Site dir : MISSING (not found on any other platform of this account)
[EXPLAINED] Client link: absent under /data/disk/o3/clients/octopus556097/
[EXPLAINED] Diagnosis : GHOST ALIAS -- …
[EXPLAINED] Fix : …
Where it appears
| Invocation | [EXPLAINED] |
|---|---|
autosymlink (dry) |
Yes, when NOT CLEAN. |
autosymlink --batch-if-clean |
Yes — from the dry pass, the one that exits 10. |
autosymlink --site <url> --apply |
Yes, when the per-site dry-run declines to apply (the Provision install/clone hook path). |
autosymlink report |
No — report mode raises no findings. |
| A CLEAN run | No — nothing to explain. |
On the nightly path it reaches the operator as the tail of the Details Report
mail, subject NOT CLEAN (manual review required) — see
Nightly automation. On demand:
autosymlink dry | sed -n '/EXPLAINED/,$p'
What it is allowed to do
The section is strictly read-only — it changes nothing, and reports only what
it can read from the filesystem. It never calls drush: autosymlink runs as
root with HOME=/root, so drush would resolve /root/.drush rather than the
account's /data/disk/<account>/.drush; Provision refuses to run as root; and a
root-run drush would leave root-owned cache and log files behind in an
oN-owned tree. See Provision backend.
Probing happens only when there is a finding to explain, so a CLEAN run pays nothing for it.
The finding codes
| Code | What tripped |
|---|---|
SITE_PATH_DIR_ABSENT |
The alias names a site_path that is not on disk — the common one, expanded below. |
ALIAS_NO_SITE_PATH |
No site_path line could be read from the alias: truncated, malformed, or a non-site context (a platform_* alias, say) that happens to have a vhost under the same name. |
SYMLINK_EMPTY_OR_BROKEN |
The in-site files/private path is a symlink whose target readlink could not read. |
SYMLINK_UNKNOWN_PATTERN |
The link resolves but points outside the expected …/static/files/<site>/<type> layout, so the owner cannot be inferred. Reported, never touched — repointing it blindly could strand live data. |
BREAK_SHARING_SOURCE_MISSING |
The site's files are shared from another site's store and the source to copy from is gone, so the un-share would fail. |
SPACE_SRC_DIR_MISSING |
The directory to move or copy does not exist. |
SPACE_SIZE_UNKNOWN |
du could not measure the source — usually unreadable, so check ownership. |
SPACE_TARGET_AVAIL_UNKNOWN |
df could not read the target filesystem — usually a mount that is not there. |
SPACE_INSUFFICIENT |
The transfer does not fit. Reservations are cumulative per filesystem, so several planned copies are counted together rather than each "fitting" alone. |
Only SYMLINK_UNKNOWN_PATTERN is an edge case; the other eight are
errors. That is why the stock verdict reads "edge cases and/or errors
detected" — the [EXPLAINED] header says which actually fired.
Orphaned stores never make a dry run NOT CLEAN: orphan handling belongs to report and apply modes, not dry. See Orphans & archiving.
The ghost alias
SITE_PATH_DIR_ABSENT is Ægir context outliving its site — a Drush alias, plus a
vhost, since the sweep only reaches a site that has both. The section walks
platform → sites/ → the site's own directory, and the first level that fails
names the cause:
| Verdict | Observed | Cause |
|---|---|---|
INCOMPLETE ALIAS |
No resolvable root, in the alias or its platform alias. |
A corrupt or truncated alias. |
PLATFORM GONE |
The platform root directory is missing. | Deleting a platform that still had sites removes the root and the platform's own alias, but leaves every site's alias and vhost behind — so expect one finding per site of that platform. A platform moved or removed outside Ægir is indistinguishable from disk. |
BROKEN PLATFORM |
Platform root present, but no sites/ tree. |
An incomplete or half-built platform — not a usable docroot. |
RENAME LEFTOVER |
The alias file's name differs from its own uri. |
A rename/migrate writes one alias file per name and only unlinks the current one; the live site runs under the other name. |
MIGRATE STRANDED |
The site's directory is found on a different platform. | An interrupted migrate — the alias is re-pointed at the new platform before the new directory is deployed. The data is intact; delete nothing. Which platform is the newer one cannot be told from disk. |
STALE ALIAS |
The alias's cached root disagrees with its platform's root. |
The alias was written against a platform layout that no longer holds. |
GHOST ALIAS |
The platform is healthy and still hosts other sites, but this site's directory exists nowhere. | An install or clone that failed or was rolled back, or a directory removed outside Ægir. |
An interrupted site delete is ruled out as a cause of a ghost: delete unlinks the alias first and removes the directory second, so a half-done delete leaves the opposite — a directory with no alias, which is an orphan store, not a ghost.
The three facts that decide what to do
Every ghost finding also reports:
- Files store — whether
static/files/<site>still holds data, i.e. whether there is anything to lose. A site delete never removes the store, so a store can outlive its site. - nginx vhost — an enabled vhost roots at the platform, not at
site_path, so nginx keeps answering for the name with no site behind it and visitors get errors rather than a disabled page (nginx -tstill passes). A disabled site instead has an inert placeholder vhost. - Client link —
clients/<client>/<uri>, which Provision points atsite_pathon install and on verify. Present but dangling means the directory existed once (a rolled-back install/clone, or a later removal); absent means it never got that far.
Deleting the alias by hand is not a fix
The alias file is only Provision's cache of the front-end database; the next
Verify task writes it straight back. autosymlink never removes an alias itself,
and neither should you — resolve the context in the Ægir front-end instead:
delete the site, or re-run the failed task. A ghost that survives repeated nights
therefore implies no successful verify has run for it since it formed.
Confirmed ghosts can self-heal
The nightly maintenance can resolve a confirmed ghost finding on its own: with
_GHOST_SITES_CLEANUP=YES the ghost-site
reaper moves the leftover alias and vhost
aside to the account's undo/ dir, after which the next dry run no longer sees
the site and can be CLEAN again. It classifies each candidate first — a
front-end/system alias, a missing platform root, or a directory found on another
platform (this page's PLATFORM GONE / MIGRATE STRANDED verdicts) is left for
operator review and never touched, and the run stays NOT CLEAN until an operator
resolves it. Any other verdict whose site_path is gone while the platform
directory exists — GHOST ALIAS itself, but also a RENAME LEFTOVER or a
registration on a BROKEN PLATFORM — is treated as a per-site leftover and
reaped the same reversible way. When the site's record still exists in the
account's own Ægir front-end, the account owner also gets a throttled email
notice with the exact panel steps (_GHOST_CLIENT_NOTIFY), since only deleting
the record there is final; a ghost whose node was already deleted is cleaned
silently.
Fixing the rest
BREAK_SHARING_SOURCE_MISSING— the finding checks the owning account's.archivedpile, which is where the source usually went: a deleted site's leftover store archived aside by an earlier sweep pulls the data out from under a sharing site's symlink. If a copy is listed, restore it rather than reaching for backups. Cross-account shares are resolved against the account the link actually points into, not the site's own.SYMLINK_UNKNOWN_PATTERN— the finding says whether the target resolves and how big it is (relative targets are resolved against the link's own directory). If the link is intentional, move the data under the per-site store and relink so the sweep can manage it; if it is a leftover, remove it and verify the site.SPACE_INSUFFICIENT— nothing is wrong with the site. The.archivedpile is never pruned automatically and is usually the cheapest space to reclaim; the report run lists its size per account.
Verify
# Explain the current verdict (read-only, safe any time):
autosymlink dry | sed -n '/EXPLAINED/,$p'
# The gate itself — batch runs only on _LAST_MODE=DRY + _LAST_STATUS=CLEAN:
cat /var/log/boa/autosymlink.state
# One site's full history and current state:
symlinkinfo shop.example.com
Related
- Tools reference —
autosymlinkmodes, the clean-dry gate and the state file,symlinkinfo. - Nightly automation — the
NOT CLEAN (manual review required)mail this section is delivered in, and--debugfor when the run does nothing at all. - Orphans & archiving — orphan stores, the
.archivedlayout and operator-only pruning. - Overview — the store layout and safety model.
- Site cloning — the clone unshare behind the narrow single-site path.
- Task failures — the task-log side of a failed install or clone, which is what leaves most ghosts behind.