Entity & service model
Aegir's state lives in a handful of Drupal node types: server,
platform, site, client, plus the task node that drives
every transition. This page is the operator-side reference for those
entities — their on-disk shape, their real status constants, their
permissions, and the provision-* backend each one maps to. The task
mechanics are in Task queue engine.
Server / service / service-type
A server is any host that provides one or more services; each service has a service type (the implementation). This three-layer model lets Aegir express heterogeneous topologies without hardcoding "MySQL on localhost, Nginx everywhere." In BOA practice most installs are flat (web + db on one Devuan VM), but the model still carries the topology.
Server (host)
├── Service: HTTP
│ └── Service type: Nginx ← BOA always uses Nginx
└── Service: Database
└── Service type: MySQL/Percona ← BOA always uses Percona
The task queue is not a server service. Aegir's hook_hosting_service
types are web / db / SSL only; there is no "queue" service type. BOA
processes tasks with a cron job on the Master (runner.sh →
hosting-dispatch), not a server-attached daemon.
The hosting modules behind this:
hosting_server— base server node type + admin UI;hosting.ip.inchandles per-server multi-IP.hosting_db_server— DB-server node form + the MySQL service interface. Every BOA host has one db_server pointing at local Percona; remote DB hosts use the same node type.hosting_web_server+hosting_nginx+hosting_ssl— the web-service backend.hosting_nginxbinds the abstract web service to the concrete Nginx config generation inlib/functions/nginx.sh.inc;hosting_sslruns cert issuance/renewal/toggle as Aegir tasks.
Each server node has a matching Drush alias on disk under .drush/:
server_master.alias.drushrc.php (master server) and
server_localhost.alias.drushrc.php (db server, usually the same host).
These carry the context data Provision reads for every provision-* call.
The distributed-topology modules hosting_web_cluster / hosting_web_pack
ship in the fork but are N-graded in BOA — not exercised on a typical
install (see Discontinued features).
Platform
A platform is a Drupal codebase on disk, hosting one or more sites.
A custom (UI-uploaded) platform lives under
/data/disk/oN/static/platforms/<subdir>/ (the instance's
hosting_platform_base_path is <root>/static/; custom platforms go in
per-platform subdirectories of static/platforms/). A BOA-bundled platform
lives under /data/disk/oN/distro/NNN/<codebase>/, where NNN is a
zero-padded numeric revision counter (the same _nextnum mechanism as the
Hostmaster codebase, not a catalogue symbol) and <codebase> is the
built Drupal tree. Which codebases get built is selected by the per-instance
platform token list (_PLATFORMS_LIST / static/control/platforms.info,
e.g. DE2 DX5 SOC UC7), but those tokens never appear as the directory name.
The tokens are BOA's own platform symbols — base Drupal cores (DE1–DE3
for D11.x, DX0–DX6 for D10.x, DL9/DL7/DL6 for D9/D7/D6) plus
distribution symbols (CK1/CK2/CK3, SOC, UC7, …), or the keyword
ALL for every available platform. A platform tree is the standard Drupal
layout — index.php, modules/, themes/, profiles/, and a sites/
tree with all/ (shared modules/themes/drush) and per-site folders.
Platform states surfaced in the UI: Queued (new, awaiting Verify),
Verified (usable), Locked (no new sites; existing sites still
work — HOSTING_PLATFORM_LOCKED), Deleted (soft-deleted, record kept).
Note that "locked" is a platform concept: hosting_platform.module
defines HOSTING_PLATFORM_LOCKED = -1. There is no site-level locked
state.
BOA treats a platform as immutable once any site is hosted on it. In-place Composer/Drush changes to a live platform's code (adding modules, upgrading core) are unsupported; the supported lifecycle is build a new platform, migrate sites to it, delete the old. See Migration & cloning.
Site
A site is a deployed Drupal instance, referencing a platform, a DB server, a client (ownership), and zero or more aliases.
Status constants
hosting_site.module defines exactly four status constants — there is
no INSTALLED and no site-level LOCKED:
HOSTING_SITE_DELETED = -2 // soft-deleted (record kept, files gone)
HOSTING_SITE_DISABLED = -1 // operator-disabled
HOSTING_SITE_QUEUED = 0 // new, awaiting Install
HOSTING_SITE_ENABLED = 1 // installed + enabled (the "live" state)
A site reaches the live state as HOSTING_SITE_ENABLED (value 1), not a
fabricated HOSTING_SITE_INSTALLED. Each transition is driven by a Task
node:
QUEUED ─→ (Install task) ─→ ENABLED
ENABLED ─→ (Disable task) ─→ DISABLED
ENABLED ─→ (Delete task) ─→ DELETED
ENABLED ─→ (Migrate task) ─→ ENABLED (on a new platform)
ENABLED ─→ (Backup task) ─→ ENABLED (tarball generated)
On-disk site files
Per site, under <platform-root>/sites/<domain>/: settings.php (Drupal
site settings, BOA-generated), drushrc.php (Aegir Drush config),
modules/ / themes/ / files/ / private/.
On current BOA, a new site installed on an Octopus (/data/disk)
account gets files/ and private/ moved into the owning account's static
store — /data/disk/<account>/static/files/<domain>/{files,private} — and
replaced with symlinks as part of installation: Provision's install hook
delegates the root-privileged move to the autosymlink tool. Scoped to
/data/disk accounts only (the master hostmaster account keeps plain
directories) and fail-open — any failure leaves plain real directories and
the install still succeeds. Kill-switches: box-wide
/data/conf/disable_native_files_symlink.cnf or per-account
static/control/no_native_files_symlink.info. Existing sites whose
files/private are still real directories are not auto-converted by
a normal Install/Verify. Full subsystem detail — store layout, tools,
nightly automation — in Files symlinking.
The optional per-site override surface is a boa_site_control.ini placed
in the site's modules/ subfolder
(sites/<domain>/modules/boa_site_control.ini) — copied from the
/data/conf/default.boa_site_control.ini template, which a
Verify seeds into the site as default.boa_site_control.ini for reference.
Edits take ~60 s to apply (FPM opcache). Use that INI for overrides;
settings.php itself is regenerated on every Verify, so direct edits to
it are wiped.
Permissions & ownership (BOA values)
The per-site permissions matrix as BOA actually sets it (verified against
provision-private, not the upstream architecture doc — BOA diverges from
upstream on two of these):
| Path | Ownership | Mode | Note |
|---|---|---|---|
settings.php |
aegir:www-data |
0440 (-r--r-----) |
Web server reads, cannot write. |
drushrc.php |
aegir:aegir |
0440 (-r--r-----) |
Set by Provision/Config/Drushrc.php ($mode = 0440). |
files/ |
aegir:www-data |
02775 |
Setgid for group inheritance. |
files/* |
www-data:www-data |
0644 |
Web-server-created files. |
private/ |
aegir:www-data |
02775 |
Same model as files/. |
sites/all/{modules,themes,libraries} |
aegir:aegir |
02775 |
Set by provision_drupal.drush.inc. |
Two upstream-vs-BOA divergences to note: upstream's architecture doc lists
drushrc.php as 0400 and files/ as 2770, but BOA uses 0440 and
02775 respectively. Use the BOA values above. If permissions drift
(Composer wrote with wrong ownership, etc.), the operator
fix-drupal-{platform,site}-{permissions,ownership}.sh scripts restore
the canonical state.
Client
A client owns one or more sites (hosting_client). It powers
multi-tenancy access control — which UI user may administer which sites —
ties into BOA's per-Octopus tenant isolation (each Octopus instance has
its own clients, isolated from other instances on the same host), and
backs per-client quotas (hosting_quota). A single-tenant install does
not need clients; a multi-tenant install makes every external customer a
client so per-client access control prevents cross-tenant visibility.
Packages (module & theme registry)
Alongside the server/platform/site/client nodes, Aegir keeps a registry of the
code on your platforms. A package (hosting_package, a package node) is
one installable unit — a module, theme, or install profile — tracked
network-wide. A package instance (hosting_package_instance) records that
package as it exists on one platform or site: its version, a computed version
code, its installed schema version, its enabled/disabled status, and the
release it came from. A third small table, hosting_package_languages, tracks
translations.
Nothing populates this by hand. Platform Verify enumerates the codebase and
calls hosting_package_sync() / hosting_package_instance_sync() for the
platform's base, sites/all, and each profile
(hosting_platform.drush.inc:76-131); site Verify records a per-site instance of
what that site has enabled. The registry is the data behind two visible
behaviours: the migrate/clone
upgrade-safety comparison
(which reads instance schema versions to decide where a site may move), and the
platform node's module/theme listing.
On Drupal 10+ / Composer platforms the registry still populates — the schema
version comes from Drupal's update.update_hook_registry service rather than the
D7 API — so package tracking is not silently empty on modern cores, though the
detected package set is core-version dependent.
Entity → backend mapping
Every entity has a Provision backend counterpart that runs when a task dispatches:
- Sites —
provision/platform/{install,backup,clone,migrate,…}.provision.inc. A per-site backup writes a flat tarball<backup_path>/<uri>-<timestamp>.tar.gz(wherebackup_path = <aegir_root>/backups), e.g.~/backups/foo.example.com-20260623.101500.tar.gz— not a per-site<site>/<timestamp>.tar.gzsubdirectory. - Platforms —
provision/platform/verify.provision.incplus theplatform/drupal/*per-Drupal-major helpers. - Servers —
provision/Provision/Context/server.phpplus the service-type backends underprovision/db/andprovision/http/.
The CLI verbs that operate on these entities take Drush aliases, not
bare domains. For example provision-clone is
drush @site provision-clone @new_site @platform_name, where @new_site
must be a Drush alias already generated by provision-save — a bare
target domain will not work.
Renaming the Aegir hostname (renameaegirhost)
When a host's FQDN changes, the Aegir hostname baked into Drush aliases,
Nginx vhosts and the Aegir DB must follow. BOA ships a single universal
tool, renameaegirhost, that renames in place for either the Master
or an Octopus account — the --aegir-root argument selects which:
renameaegirhost --aegir-root /var/aegir ← BOA Master
renameaegirhost --aegir-root /data/disk/oN ← Octopus account oN
It validates the path as a real Aegir root, then: detects the old
hostname from <aegir-root>/.drush/server_master.alias.drushrc.php and the
new one from hostname -f; rewrites every Drush alias under
<aegir-root>/.drush/; renames and rewrites the vhost files under
<aegir-root>/config/server_master/nginx/vhost.d/; dumps the Aegir DB,
sed-replaces the hostname and re-imports; reloads Nginx; flushes the
Drush cache and runs a 5-pass Aegir task queue to regenerate aliases,
vhosts and db-host entries from the updated database. Flags: --dry-run
(print planned changes, modify nothing) and --force-old FQDN (override
the auto-detected old hostname). The tool assumes BOA cron is stopped —
the caller must stop it first.
There is no separate renamemaster tool: an earlier transitional
renamemaster was folded into the universal renameaegirhost. Migration
tooling calls it automatically — an xoct import and an xmass cutover
both invoke it (the cutover runs it for /var/aegir and every
/data/disk/oN), so a host move regenerates contexts without a manual
rename. See Migration & cloning.
Related
- Architecture overview — the layered model and file-system layout.
- Task queue engine — how each status transition is actually driven.
- Multi-Octopus model — per-instance isolation and the client/tenant mapping.
- Database — Percona host-side setup that
hosting_db_serverreferences. - Nginx internals — the Nginx generation that
hosting_nginxreferences. - Reference appendix — consolidated node-type and permission tables.