Web & search stack internals

The web-serving half of BOA: how Provision turns an Aegir site node into an Nginx vhost + a settings.php, where the edge-map wall that fronts every request is declared, and how the Barracuda installer lays down the Solr search backends those vhosts talk to. Two languages meet here — the vhost/settings generator is Drush 8 / PHP 5.6-floor PHP inside the Provision fork, the Solr installer is Barracuda-side bash inside boa-private — and a change on either side has to survive the other's assumptions. If you touch server.tpl.php, a settings template, or solr.sh.inc, this is the wiring your change lands in.

The render pipeline in one pass

Every Nginx config file BOA writes goes through the same Provision_Config machinery, driven by the nginx service class (Provision_Service_http_nginx, http/Provision/Service/http/nginx.php:3). init_server() registers which config classes render at server vs site scope (nginx.php:23-25,39-40), and save_server()/verify_server_cmd() probe the running box — nginx -V feature flags, socket-vs-port PHP-FPM mode, the /data/conf/global.inc BOA-mode marker — and stash the results as server properties the templates read (nginx.php:44-160). Each registered class is a Provision_Config subclass whose write() runs the fixed cycle in the base (Provision/Config.php:195): process() populates $this->data, load_template() resolves the .tpl.php (through the provision_config_load_templates[_alter] hooks, Config.php:117-160), and render_template() eval('?>' . $template)s it with $this and $data in scope (Config.php:169-186) before file_put_contents writes the file — then Provision_Config_Http::write() ->sync()s it to the target server (http/Provision/Config/Http.php:10-13).

The three server/site classes and what they emit:

Class Template Renders to Contains
Provision_Config_Nginx_Server (Nginx/Server.php) server.tpl.php <config_path>/nginx.conf (Http/Server.php:21-24) the http-block edge maps + limit_*_zones
Provision_Config_Nginx_Inc_Server (Nginx/Inc/Server.php) Inc/vhost_include.tpl.php <include_path>/nginx_vhost_common.conf (Http/Inc/Server.php:41-44) the per-request guard chain every vhost includes
Provision_Config_Nginx_Site (Nginx/Site.php) vhost.tpl.php (vhost_disabled.tpl.php when down) <http_vhostd_path>/<uri> (Http/Site.php:13-19,37-39) the site server block, DB creds, include nginx_vhost_common.conf

The SSL variants (Nginx/Ssl/*, registered by nginx/ssl.php) render the quic-enabled listeners; the subdir feature adds Provision_Config_Nginx_Subdir

  • _SubdirVhost only when provision_hosting_feature_enabled('subdirs') (nginx.php:37-41). All of them extend the same base, so the pipeline above is invariant — only the template and filename() change.

Both server-scope classes also splice in drush_command_invoke_all('provision_nginx_server_config', …) / _vhost_config output (Nginx/Server.php, Nginx/Site.php), the extension seam documented on the Aegir backend APIs topic — that is how a hosting_* module injects config without editing a template.

Where the four leaves sit

Aegir site node  ──(verify task)──▶  Provision http service
                                        │
   ┌────────────────────────────────────┼───────────────────────────────┐
   │ server.tpl.php  (once per server)   │ vhost.tpl.php / vhost_ssl      │  settings template
   │  http { ... maps + geo + zones }    │  (once per site)              │  (once per site)
   │  $is_banned, $is_cms_probe,         │  server { listen; server_name;│  settings.php:
   │  $is_ai_*, $cache_uid,              │    fastcgi_param db_* creds;  │   literal db creds,
   │  $boa_i18n_* ...                    │    include nginx_vhost_common }│   global.inc chain,
   │        ↓ [1 edge maps]              │        ↓ [2 vhost generator]  │   local.settings.php
   │  Inc/vhost_include.tpl.php          │                               │        ↓ [3 settings wiring]
   │   guard chain: if ($is_*) return 444│                               │
   └────────────────────────────────────┴───────────────────────────────┘
                                        │
                          php-fpm ──▶ Drupal ──▶ Search API / Solr
                                                      ↑ [4 Solr installer: solr.sh.inc]
  • [1] Edge maps are declared in server.tpl.php (http scope, once per server) and enforced in vhost_include.tpl.php (per request, in every vhost). Splitting declaration from enforcement is deliberate: a map is a cheap constant-time lookup, so the guard chain is a flat list of if ($is_x) return 444; with no per-request regex.
  • [2] The vhost generator is vhost.tpl.php / Ssl/vhost_ssl.tpl.php plus the Provision_Config_Nginx_* classes that render them — the per-site server block, the DB-cred fastcgi_param injection, and the include that pulls in the shared guard chain.
  • [3] settings.php wiring is the Drupal-side companion: BOA writes the DB creds literally into settings.php (BOA turns Aegir's $_SERVER db-cloaking offcloaked_db_creds() returns FALSE), plus the three-level global.inc → platform → local.settings.php include chain.
  • [4] The Solr installer is Barracuda-side bash — solr.sh.inc lays down the Solr 4/7/9 backends and their JVM/service wiring that Search-API vhost traffic ultimately reaches.

Hold that picture; each leaf zooms into one box.

Pages in this topic

  • Vhost generator (Provision Nginx config classes) — the Provision_Config render cycle end to end: the nginx service class (nginx.php) and its init_server/save_server/verify_server_cmd feature probing, the server/inc/site class split and their filename() targets, the eval-based template render, the disabled-vhost template swap (Http/Site.php:37-39), the subdir generator's per-alias write() loop (Nginx/Subdir.php), the nginx_vhost_common.conf legacy symlinks (Http/Inc/Server.php:14-37), and the post.d operator-override drop points the rendered vhost includes. Note for accuracy: the per-site rendered SSL listener is listen {ip}:{port} quic; without reuseport (Ssl/vhost_ssl.tpl.php:64-66) — reuseport belongs only to the wildcard master listener, not the per-site vhost.
  • Edge map mechanics (server.tpl.php) — the full http-block map/geo catalogue and the two-stage declare-then-enforce idiom: the geo $remote_addr $is_banned ban wall (server.tpl.php:473), the $is_cms_probe foreign-CMS whole-segment probe map (server.tpl.php:458), the AI-crawler class maps and their limit_req_zone rate keys (server.tpl.php:314-424,109-111), the $cache_uid session map (server.tpl.php:889) that authoritatively decides anon-vs-authenticated, the $boa_i18n_guard/$boa_i18n_path/$boa_is_anon$boa_i18n_anon_key chain and its limit_conn boa_i18n_anon cap (server.tpl.php:924-955,120), and why every one of these is a map even when it looks like a constant (so nginx -t never sees an undefined variable). Enforcement of each lives in the guard chain (vhost_include.tpl.php:183-211, return 444).
  • settings.php wiring (Drupal settings templates)Provision_Config_Drupal_Settings (Config/Drupal/Settings.php) and the per-major template selection (provision_drupal_settings_{6..11}.tpl.php, Settings.php:18-68): the $this->cloaked DB-credential switch that on BOA resolves FALSE (the nginx service's cloaked_db_creds() returns FALSE, nginx.php:17-19), so the template writes literal creds via its else branch (settings_7.tpl.php:95-117) — not the stock-Aegir $_SERVER cloak branch — the utf8mb4/hash-salt/db_type mysqli→mysql normalisation, the provision_drupal_config extension hook feeding $extra_config, and the fixed include cascade /data/conf/global{,-N}.inc → platform settings.php/platform.settings.php → per-site local.settings.php (settings_7.tpl.php:200-224), the last of which Provision creates blank and locks to 0440 (Settings.php:95-114).
  • Solr installer (solr.sh.inc) — the Barracuda-side search backend layer: _if_install_upgrade_solr (solr.sh.inc:447), invoked from BARRACUDA.sh.txt:578 when /root/.deny.java.cnf is absent, and its three version ladders — Solr 4 on Jetty 9 (_if_solr_four, port 8099), Solr 7 (_if_solr_seven, port 9077), Solr 9 (_if_solr_nine, port 9099) — each gated on an SRn token in _XTRAS_LIST, an existing data dir, or a hosted-sys marker (solr.sh.inc:454-477). Covers the pinned versions in BARRACUDA.sh.txt (_SOLR_4_VRN/_SOLR_7_VRN/_SOLR_9_VRN/_JETTY_9_VRN, :135-137,65), the _get_dev_arch mirror fetch, the install_solr_service.sh invocation, the per-version-file idempotency guards (solr-<VRN>-version.txt), the JDK-pinned /etc/default/solrN.in.sh rewrite (Java 11/17/21 by _OS_CODE), the _UP_JDK restart path, and where manage_solr_config.sh fits for core config sync.

Adjacent developing topics

  • Aegir backend APIs — the provision_nginx_server_config / provision_nginx_vhost_config / provision_drupal_config hooks these templates invoke, and the Provision template-alter chain a hosting_* extension uses to add config without patching a .tpl.php.
  • Install & staged-setup internals — how the Provision codebase (and the Solr backends) get onto a box in the first place: the staged AegirSetup* engine and the /opt/tmp/make_local build tree.
  • Monitor & abuse-guard internals (code) — the scan_nginx scorer that reads the access log these vhosts write and reacts to the 444s the guard chain and the $boa_i18n_anon cap emit; the ban decisions it makes land back in the $is_banned geo declared here.

Reference

  • Variables and Commands — the consolidated _VAR and CLI tables, including the _SOLR_*/_JETTY_*/_XTRAS_LIST knobs the installer reads. The Nginx map variables ($is_banned, $is_cms_probe, $boa_i18n_*, $cache_uid, …) are deployed-vhost contract surface: add, never rename or remove.
  • Discontinued features — retired search/web pieces (older Solr/Jetty generations, superseded template mechanics) land there; check it before documenting anything found only in old trees or logs.