ols-htaccess-watcher.sh matched .htaccess by basename only, so ANY .htaccess
under a tenant (WordPress plugin guard files, not just the docroot OLS reads)
triggered a full graceful restart. Measured on whp01 over 24h: 63 restarts,
0 of them from a docroot .htaccess actually changing — all from Wordfence/W3TC/
WPForms/etc. self-healing files, mostly on tenants that aren't even on this
tier. Now matches the full path (%w%f) against */public_html/.htaccess, the
only .htaccess OLS ever reads, and logs which path triggered each restart.
entrypoint-shared-ols.sh's 3s supervisor poll called `lswsctrl status`, which
appends a line to lsrestart.log on every invocation. Measured on whp01:
1,819,286 status lines vs 2,429 real restarts in a 96 MB, never-rotated log.
ols_running() now checks the process table directly (pgrep -f 'lshttpd -
main', verified against the litespeedtech/openlitespeed base image) instead of
shelling out to a logging tool on a fixed timer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses the local code-review on the OLS-tier images:
- [HIGH] ols-htaccess-watcher.sh: the debounce drain read ALL inotify events
unfiltered, so on a busy multi-tenant server it never timed out and the
restart was STARVED (rewrite changes silently never applied). Now coalesces
with a hard DEBOUNCE-bounded window. Verified under continuous noise.
- [HIGH] render-shared-ols-config.sh: built httpd_config.conf in-place across
several appends, so a concurrent OLS restart (watcher) or parallel render
could read a half-written config and 503 the whole tier. Now flock-serialized,
built in a temp file and atomically moved into place; refuses to publish empty.
- [MED] render + entrypoint: replaced recursive chown of the whole conf tree
(O(N-sites) on every single-site change / boot) with a targeted chown of just
the file written.
- [MED] render: parse site.meta with sed instead of sourcing it (do not execute
panel-written data as shell).
- [cleanup] removed the unused configs/shared-ols/vhconf.tpl (the panel copy is
the single source; the image never read it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One OLS container fronting many tenants' detached cac-lsphp sidecars — the
OLS analogue of shared-httpd. Runs NO PHP locally; every site's PHP goes to
its own sidecar over LSAPI (extProcessor type lsapi, address <sidecar>:9000).
Key design fact (established by PoC): OLS has NO top-level 'include' directive,
so render-shared-ols-config.sh assembles httpd_config.conf from the panel's
per-site files (vhconf.conf + site.meta) at boot and on every change — the
'include' OLS lacks. Per-site detail uses the OLS-native configFile +
vhost-scoped extprocessor model. LSCache is module-level (a configFile-loaded
vhost rejects a bare cache{} block); the WP LiteSpeed plugin controls
cacheability via X-LiteSpeed-Cache-Control headers.
- Dockerfile.shared-ols: litespeed base + inotify-tools/envsubst/openssl,
admin bound to loopback, :80/:443 self-signed, healthz HEALTHCHECK.
- entrypoint-shared-ols.sh: cert + health vhost + render + watcher, then
daemon-mode OLS supervision (reused from cac-litespeed so self-restarts
don't kill PID 1).
- render-shared-ols-config.sh: strip stock (incl local lsphp) + append base +
per-site stanzas + listeners with all maps + catch-all health vhost.
- ols-htaccess-watcher.sh: inotify debounce+floor -> lswsctrl restart (spec 5.3).
- configs/shared-ols/{httpd_config_base,vhconf}.tpl.
- CI: Build-Shared-OLS job.
Verified locally end-to-end: zero-site boot healthy on :443; add site via the
panel contract -> Host-routed to the right sidecar (SAPI=litespeed); real
client IP + HTTPS behind X-Forwarded headers; LSCache miss->hit; .htaccess
change triggers graceful restart; unknown Host hits health catch-all (200).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>