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:
48
configs/litespeed/httpd_config.tpl
Normal file
48
configs/litespeed/httpd_config.tpl
Normal 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: HTTP→HTTPS 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}, *
|
||||
}
|
||||
}
|
||||
43
configs/litespeed/lsphp-overrides.ini
Normal file
43
configs/litespeed/lsphp-overrides.ini
Normal 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
|
||||
87
configs/litespeed/site-template.tpl
Normal file
87
configs/litespeed/site-template.tpl
Normal 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
|
||||
}
|
||||
}
|
||||
77
configs/litespeed/vhconf.tpl
Normal file
77
configs/litespeed/vhconf.tpl
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user