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
| Site | Type | What it drills |
|---|---|---|
| default (catch-all) | static navigation page | lowest-risk catch-all discipline (ch. 20) |
| www main site | dynamic (script + template + db) | mounting the Project 1 deliverable |
| api subdomain | dynamic (REST) | host-based routing split + independent limits |
| docs site | pure static (devlang=static) | the zero-script site shape |
| admin site | dynamic (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
| Dimension | Standard |
|---|---|
| Routing | all five sites hit precisely + the default catch-all + consistency across the three sources |
| Isolation | a single-site reload spills zero side effects; Custom keys never interfere |
| Security | three overlapping layers on the admin site; the full 44.4 checklist passes |
| Ops | the inspection route / certificate ledger / per-site logs all work |
| Deployment | systemd + 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
| Point | Conclusion |
|---|---|
| Topology | five sites mixed in one process: static and dynamic split apart, the management plane on the intranet |
| Reload discipline | minimal blast radius: submit per site, roll shared changes through in order |
| Operations | inspection route + certificate ledger + per-site logs |
| Scale check | teaching five → production 14 (home as living proof) |
API references used in this chapter
Further reading on xrt
- xrt · x509 — certificate expiry parsing (the ledger feature)