Multi-site
Many first-class domains on one lazysite instance, with one login.
Multi-site
One lazysite instance serves many domains (SM110/SM151). Each domain is rooted at its own content subtree and is, to the outside world, a complete site - its own home page, navigation, theme, canonical URL, sitemap, feeds and search. Behind the scenes there is one docroot, one auth store, one manager and one MCP endpoint: one login and one AI credential drive every domain.
Configuration
Domains are declared in lazysite.conf: alias_hosts lists the served
hosts, and alias.<host>.<key> lines override per host. The Domains
manager page and the lazysite-domains CLI write the same lines.
site_name: Agency Home
alias_hosts: clienta.example, clientb.example
alias.clienta.example.site_name: Client A
alias.clienta.example.content_root: sites/clienta
alias.clienta.example.site_url: https://clienta.example
alias.clienta.example.theme: harbour
alias.clienta.example.nav_file: sites/clienta/nav.conf
Only a strict whitelist of presentation and routing keys may vary per
host: site_name, theme, layout, nav_file, search_default,
content_root, site_url, lang and lang_group. The Host header is
request-supplied, so security-relevant keys (auth, manager, WebDAV,
update) can never be overridden per host, and an undeclared or malformed
host simply gets the base configuration.
Per-domain content roots
content_root names a docroot-relative directory that becomes the
domain's /. The boundary is enforced at request time: the root must
resolve (symlinks included) inside the docroot and never into the
lazysite/ management tree. An invalid root degrades that one host to
the docroot with a warning - it never takes the instance down or exposes
a sibling's tree. A host declared without a content_root serves the
default site.
Per-domain registries, search and cache
- Sitemap, robots, feeds and
llms.txtare generated per content root, written into that root, using the domain's ownsite_url - Search (
scan:sources) is boxed to the requesting domain's root, so a search on one domain never returns another's pages - A host with no root of its own scans the whole docroot but skips every declared content root, so the default host never enumerates a client subtree
- The render cache is host-keyed - each domain reads and writes its own cache slot, listed and cleared per host on the Cache page
The shared management plane
The lazysite/ tree (config, auth, ACLs, themes, layouts, logs,
backups) exists once and is shared by every domain. Themes and layouts
are reused across domains; the file browser sees every subtree; the
first-party access log records the request Host so statistics can be
split per domain. The design target is an agency: one user, one AI,
N public sites - a single trust domain, not tenant isolation.
Web-server static rewrites
Rendered pages route through the processor, which also serves each
domain's static files from its content root. For production efficiency
the vhost tools generate per-Host rewrite rules from lazysite.conf, so
the web server serves each domain's real static files (images, CSS,
sitemap, feeds) directly from its subtree:
lazysite-apache-vhost rewrites --docroot /path/to/public_html
lazysite-nginx-vhost rewrites --docroot /path/to/public_html
Apache gets a mod_rewrite block (fires only when the file exists);
nginx gets a map $http_host plus a try_files line. Clean page URLs
and the /lazysite, /cgi-bin and /manager surfaces are never
rewritten - they still flow to the processor.
Notes
- A bare docroot with no configured roots behaves exactly as a single-site install - multi-site is a pure superset
- DNS, the web-server alias and TLS remain operator territory; lazysite owns only the conf and the content root
[% domain %]exposes the sanitised host to layouts, so one content root can still render differently per domain- Taking a client in-house is a move, not a rebuild: copy the subtree to a fresh instance and repoint DNS