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.shhosting-dispatch), not a server-attached daemon.

The hosting modules behind this:

  • hosting_server — base server node type + admin UI; hosting.ip.inc handles 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_nginx binds the abstract web service to the concrete Nginx config generation in lib/functions/nginx.sh.inc; hosting_ssl runs 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 (DE1DE3 for D11.x, DX0DX6 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:

  • Sitesprovision/platform/{install,backup,clone,migrate,…}.provision.inc. A per-site backup writes a flat tarball <backup_path>/<uri>-<timestamp>.tar.gz (where backup_path = <aegir_root>/backups), e.g. ~/backups/foo.example.com-20260623.101500.tar.gz — not a per-site <site>/<timestamp>.tar.gz subdirectory.
  • Platformsprovision/platform/verify.provision.inc plus the platform/drupal/* per-Drupal-major helpers.
  • Serversprovision/Provision/Context/server.php plus the service-type backends under provision/db/ and provision/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