Chapter 47 / 51
Volume 5 · Extensions, Ops & Projects

Project 3: a multi-vhost site farm (a home replica)

Operating a whole fleet of sites from one process — the last project takes the "deployment topology" itself as the subject: build a five-site mixed farm to production standards.

Overview

Positioning: the ops-oriented capstone. Ch. 20 taught the organizational method; this chapter walks the whole pipeline once at production standards — the blueprint is the home repository (a real production farm of 14 vhosts; we build a five-site teaching version).

47.1 Project goal

SiteTypeWhat it drills
default (catch-all)static navigation pagelowest-risk catch-all discipline (ch. 20)
www main sitedynamic (script + template + db)mounting the Project 1 deliverable
api subdomaindynamic (REST)host-based routing split + independent limits
docs sitepure static (devlang=static)the zero-script site shape
admin sitedynamic (intranet-bound)management-plane isolation (ch. 27/39)

47.2 Building the farm

  • Layout: the self-contained host directories of ch. 20/37 — five directories + one xs.json + one exe
  • Configuration: a single server with two endpoints (80/443) accepting everything; per-host domains, certificates and Custom knobs for the five hosts (tighter body_limit on the api site; the four static-layer knobs on the docs site)
  • Shared modules: www and api reuse modules/ (copy-style, ch. 20) — note that Custom key names on the two sites never interfere with each other
  • Admin site off the public net: the admin domain stays out of public DNS + firewall source-IP restrictions + a token (three overlapping layers)
Acceptance point 1: each of the five sites is hit correctly (Host header / SNI testing); unknown domains land on the default navigation page; the docs site leaves zero in-process script traces (no "script loaded" lines).

47.3 Single-site hot reload

The farm's hot-reload discipline is "minimal blast radius":

  • Every day: submit only the target host (the admin site's /admin/reload → xsReloadHostSubmit)
  • Verification: keep load-testing the other four sites — zero errors, zero perceived impact (unchanged sites don't participate at all — the config-level split from ch. 20)
  • Cascading updates: when a shared modules/ change touches several sites, reload them one by one in order (not all) — rolling, not in lockstep
  • Drills: certificate renewal (swapping the docs site's certificate with zero dropped connections), bad-script rollback (submit a broken script on the api site → FAILED → the old version survives intact)
Acceptance point 2: three consecutive single-site reloads under load on the other four sites, zero failures; hot certificate replacement verified by openssl comparison.

47.4 Unified inspection & certificates

  • Inspection route: the admin site implements /status — enumerate every server/host and report RunState, generation number and request counts (the ch. 11 query API + the ch. 43 patterns)
  • Certificate ledger: /status also lists each host certificate's expiry date (x509 parsing) — renewal reminders go into the ops calendar
  • Log aggregation: all five sites' logs share the [site-name] prefix; on the journald side, filter per site

47.5 The acceptance checklist

DimensionStandard
Routingall five sites hit precisely + the default catch-all + consistency across the three sources
Isolationa single-site reload spills zero side effects; Custom keys never interfere
Securitythree overlapping layers on the admin site; the full 44.4 checklist passes
Opsthe inspection route / certificate ledger / per-site logs all work
Deploymentsystemd + a maintenance window + a blue-green script (the output of the ch. 42 challenge)

Common mistakes

Reaching for all
Symptom
To change one site's copy you submit xsReloadAllSubmit — and all five sites change generation.
Cause
all is a batch transaction for "global configuration changes" (ch. 26), not a lazy default. A farm-wide generation swap = multiplied compilation + multiple GC passes, amplifying every risk.
Forgetting to sync the shared module across sites
Symptom
You fixed the ReplyJSON bug on the www site; the api site still runs the old version.
Cause
The cost of copy-style reuse (ch. 20) — every shared-module change needs a "blast list" (which sites copied it), then roll through that list site by site.

Exercises

BasicFive sites live

Build the farm per 47.2 and pass acceptance point 1.

Acceptance: acceptance point 1 fully passes.
IntermediateThe ops loop

Complete 47.3/47.4: single-site hot reload + the inspection route + the certificate ledger.

Acceptance: acceptance point 2 fully passes; /status flags certificates expiring within 30 days.
ChallengeScale to fourteen sites

Following home's real inventory, grow the farm to 14 vhosts (including several pure-static ones) and verify that the configuration and ops procedures scale.

Acceptance: startup < 3s; the inspection route covers every site; the ops docs updated.

Summary

PointConclusion
Topologyfive sites mixed in one process: static and dynamic split apart, the management plane on the intranet
Reload disciplineminimal blast radius: submit per site, roll shared changes through in order
Operationsinspection route + certificate ledger + per-site logs
Scale checkteaching five → production 14 (home as living proof)
Further reading on xrt
  • xrt · x509 — certificate expiry parsing (the ledger feature)