Add cac-litespeed image family (OpenLiteSpeed, native LSAPI)

New paid-tier per-customer image built on litespeedtech/openlitespeed:1.8.4-lsphpNN.
Matrix: 8.1-8.5. Native LSAPI suexec to customer uid, server-level LSCache,
all WP/WooCommerce extensions (memcached, redis, imagick, mbstring, etc.) baked in.

Files:
- Dockerfile.litespeed (FROM prebuilt LiteSpeed base, layers wp-cli/composer/mariadb)
- configs/litespeed/{httpd_config,site-template,lsphp-overrides}.tpl
- scripts/{entrypoint,create-vhost,detect-memory}-litespeed.sh + install-lscache-wp.sh

CI: new Build-LiteSpeed-Images matrix job. OLS_VERSION pinned to 1.8.4 (only
release with prebuilt images for all 5 PHP versions on Docker Hub).

Spec: whp/docs/superpowers/specs/2026-06-01-cac-litespeed-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 07:32:47 -07:00
parent 1756d496e5
commit 55c28a0c11
10 changed files with 711 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
## OpenLiteSpeed APPEND fragment — added to the stock httpd_config.conf
## that ships with litespeedtech/openlitespeed. Keeping the stock config
## intact preserves all the cgid/lscgid plumbing (CGIRLimit defaults,
## fileAccessControl defaults, etc.) — when we tried writing a fully
## custom httpd_config.conf, lscgid never created its IPC socket and
## every PHP request 503'd. The upstream OLS docker template uses this
## append pattern too (see setup_docker.sh in litespeedtech/ols-dockerfiles).
##
## Rendered at container start by scripts/create-vhost-litespeed.sh via
## envsubst. Templated vars: $user $domain $vhost_map_aliases.
## --- our listeners (replace stock Default :8088) ---
listener HTTP {
address *:80
secure 0
map siteVH *
## NB: HTTPHTTPS redirect is in site-template.tpl's rewrite{} block,
## NOT here — OLS 1.8 listener-level rewrites are inert for vhTemplate
## members. Don't move it back to this listener.
}
listener HTTPS {
address *:443
secure 1
keyFile /usr/local/lsws/conf/cert/self.key
certFile /usr/local/lsws/conf/cert/self.crt
sslProtocol 24
enableSpdy 15
enableQuic 0
map siteVH *
}
## --- our vhost via vhTemplate (upstream's working pattern) ---
## The template file is /usr/local/lsws/conf/templates/site.conf — written
## by create-vhost-litespeed.sh at the same time as this fragment.
vhTemplate site {
templateFile conf/templates/site.conf
listeners HTTP, HTTPS
note cac-litespeed per-customer vhost
## vhDomain: customer's domain + serveralias list + `*` catchall so
## ip-only requests (e.g. HAProxy backend health check by container_name)
## still resolve. WHP/HAProxy filters hostnames upstream no risk to
## allowing the catchall here.
member siteVH {
vhDomain ${domain}${vhost_map_aliases}, *
}
}

View File

@@ -0,0 +1,43 @@
; Production lsphp overrides — mirrors configs/prod-php.ini for the FPM
; image, adapted for LSAPI defaults. Dropped into /usr/local/lsws/lsphpNN/etc/php.d/
memory_limit = 256M
post_max_size = 384M
upload_max_filesize = 256M
max_input_vars = 2000
max_execution_time = 60
max_input_time = 120
expose_php = Off
short_open_tag = Off
display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
zend.exception_ignore_args = On
session.save_handler = files
session.use_cookies = 1
session.use_only_cookies = 1
session.use_strict_mode = 0
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 60
opcache.enable_cli = Off
output_buffering = 4096
default_charset = "UTF-8"
file_uploads = On
max_file_uploads = 20
soap.wsdl_cache_enabled = 1
soap.wsdl_cache_dir = "/tmp"
soap.wsdl_cache_ttl = 86400
soap.wsdl_cache_limit = 5

View File

@@ -0,0 +1,87 @@
## OLS vhTemplate for the per-customer vhost. Mirrors the structure of the
## upstream docker.conf template but with our paths and LSCache wiring.
## Templated vars (envsubst): $user
##
## $VH_NAME, $VH_ROOT, $DOC_ROOT, $SERVER_ROOT are OLS macros — they MUST
## stay literal in the output (not in the envsubst allow-list).
allowSymbolLink 1
enableScript 1
restrained 1
## setUIDMode 2 = DocRoot UID — lsphp suexec's to the OWNER of vhRoot.
## We chown /home/${user} to ${user}:${user} in the entrypoint, so PHP
## runs as the customer per request. Container is still the privsep
## boundary; this is the clean "scripts run as user" model.
setUIDMode 2
vhRoot /home/${user}/public_html/
configFile $SERVER_ROOT/conf/vhosts/$VH_NAME/vhconf.conf
virtualHostConfig {
docRoot $VH_ROOT
errorlog /home/${user}/logs/litespeed/error.log {
useServer 0
logLevel WARN
rollingSize 10M
keepDays 14
compressArchive 1
}
accesslog /home/${user}/logs/litespeed/access.log {
useServer 0
rollingSize 10M
keepDays 7
compressArchive 1
}
index {
useServer 0
indexFiles index.php, index.html
autoIndex 0
}
## LSCache plugin owns Cache-Control / Expires entirely server-level
## expires off so we don't double-emit headers.
expires {
enableExpires 0
}
accessControl {
allow *
}
context / {
location $DOC_ROOT/
allowBrowse 1
rewrite {
enable 1
inherit 0
autoLoadHtaccess 1
RewriteFile .htaccess
}
addDefaultCharset off
}
rewrite {
enable 1
autoLoadHtaccess 1
logLevel 0
## Force HTTPS — OLS 1.8 listener-level rewrites don't apply per-vhost,
## so the redirect lives here. The RewriteCond guards against an infinite
## loop (SERVER_PORT=80 means "this request came in on the HTTP listener,
## not HTTPS"). Per-customer .htaccess rules still apply (autoLoadHtaccess).
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [L,R=301]
}
## Per-vhost LSCache storage. The server-level `module cache` block in
## stock httpd_config.conf is already enabled (ls_enabled 1); the LSCWP
## plugin flips cache on/off per request via X-LiteSpeed-Cache-Control.
module cache {
storagePath /home/${user}/lscache
checkPrivateCache 1
checkPublicCache 1
enableCache 0
enablePrivateCache 0
}
}

View File

@@ -0,0 +1,77 @@
## Per-vhost config — rendered at container start.
## Templated vars (envsubst allow-list): $user $domain
## Anything that looks like $DOC_ROOT, $VH_ROOT, $HTTP_HOST etc. is an OLS
## runtime macro — intentionally NOT in the envsubst allow-list so it
## passes through unchanged for OLS to expand at request time.
docRoot /home/${user}/public_html
enableGzip 1
enableBr 1
errorlog /home/${user}/logs/litespeed/error.log {
useServer 0
logLevel WARN
rollingSize 10M
keepDays 14
compressArchive 1
}
accesslog /home/${user}/logs/litespeed/access.log {
useServer 0
rollingSize 10M
keepDays 7
compressArchive 1
}
index {
useServer 0
indexFiles index.php, index.html
autoIndex 0
}
scripthandler {
add lsapi:lsphp php
}
## LSCache plugin owns Cache-Control / Expires entirely — keep server-level
## expires off so we don't double-emit headers.
expires {
enableExpires 0
}
accessControl {
allow *
}
context / {
## $DOC_ROOT is an OLS macro (not a shell var). Don't add it to the
## envsubst allow-list in create-vhost-litespeed.sh or it'll expand to
## empty and break docroot resolution.
location $DOC_ROOT/
allowBrowse 1
rewrite {
enable 1
inherit 0
autoLoadHtaccess 1
RewriteFile .htaccess
}
addDefaultCharset off
}
rewrite {
enable 1
autoLoadHtaccess 1
logLevel 0
}
## Per-vhost LSCache storage. Server module cache{} block enables the engine;
## these lines tell the vhost WHERE to cache. The LSCWP plugin flips the
## cache on/off at request time via X-LiteSpeed-Cache-Control headers.
module cache {
storagePath /home/${user}/lscache
checkPrivateCache 1
checkPublicCache 1
enableCache 0
enablePrivateCache 0
}