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>
2026-06-02 07:32:47 -07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
## entrypoint-litespeed.sh — PID 1 for cac-litespeed:phpNN.
|
|
|
|
|
## Built on litespeedtech/openlitespeed:1.8.x-lsphp83 prebuilt base. Native
|
|
|
|
|
## LSAPI (no FPM proxy), one customer per container.
|
|
|
|
|
##
|
|
|
|
|
## Process supervision: starts OLS via `openlitespeed -n` (no-daemon +
|
|
|
|
|
## crash-guard, per OLS source: lshttpdmain.cpp). SIGTERM is forwarded.
|
|
|
|
|
## crond runs in the background for customer crontabs; OLS itself is the
|
|
|
|
|
## process we wait on (if OLS dies, the container exits and Docker
|
|
|
|
|
## restarts it per its restart policy).
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
: "${PHPVER:=83}"
|
|
|
|
|
: "${environment:=PROD}"
|
|
|
|
|
: "${LSCACHE_AUTOINSTALL:=1}"
|
|
|
|
|
|
|
|
|
|
export CONTAINER_ROLE="litespeed_only"
|
|
|
|
|
export PHPVER environment LSCACHE_AUTOINSTALL
|
|
|
|
|
|
|
|
|
|
## ---- env validation ----
|
|
|
|
|
if [ -z "${uid:-}" ] || [ -z "${user:-}" ]; then
|
|
|
|
|
echo "FATAL: 'uid' and 'user' env vars are required (panel sets these from WHP_UID/WHP_USER)." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
: "${domain:=localhost}"
|
|
|
|
|
export user domain
|
|
|
|
|
|
|
|
|
|
## ---- user + directories ----
|
|
|
|
|
if ! id -u "$user" >/dev/null 2>&1; then
|
|
|
|
|
## Ubuntu's useradd; mirror what the AL10 entrypoints do with adduser
|
|
|
|
|
useradd -u "$uid" -m -s /bin/bash "$user"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mkdir -p "/home/$user/public_html"
|
2026-06-02 10:53:44 -07:00
|
|
|
## Log dirs mirror cac:phpNN exactly — apache/ for web server access+error,
|
|
|
|
|
## php-fpm/ for PHP errors. OLS isn't Apache and lsphp isn't php-fpm, but
|
|
|
|
|
## the customer-facing paths stay identical so log-gathering, analytics,
|
|
|
|
|
## and the customer's "where do I find my access log?" mental model all
|
|
|
|
|
## just work without per-image-family special cases.
|
|
|
|
|
mkdir -p "/home/$user/logs/apache" "/home/$user/logs/php-fpm"
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
mkdir -p "/home/$user/lscache"
|
|
|
|
|
|
|
|
|
|
mkdir -p /tmp/lshttpd/swap
|
|
|
|
|
chmod 1777 /tmp/lshttpd
|
|
|
|
|
|
|
|
|
|
## ---- memory + lsphp pool sizing ----
|
|
|
|
|
# shellcheck source=/dev/null
|
|
|
|
|
source /scripts/detect-memory-litespeed.sh
|
2026-06-02 16:36:25 -07:00
|
|
|
echo "Container memory: ${CONTAINER_MEMORY_MB}MB | LSAPI_CHILDREN=${LSAPI_CHILDREN} | PHPVER=${PHPVER}"
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
|
|
|
|
|
## ---- self-signed cert (idempotent) ----
|
|
|
|
|
mkdir -p /usr/local/lsws/conf/cert
|
|
|
|
|
if [ ! -f /usr/local/lsws/conf/cert/self.crt ]; then
|
|
|
|
|
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
|
|
|
|
|
-keyout /usr/local/lsws/conf/cert/self.key \
|
|
|
|
|
-out /usr/local/lsws/conf/cert/self.crt \
|
|
|
|
|
-subj "/CN=${domain}" 2>/dev/null
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## ---- render httpd_config + vhconf from templates ----
|
|
|
|
|
/scripts/create-vhost-litespeed.sh
|
|
|
|
|
|
2026-06-02 10:53:44 -07:00
|
|
|
## ---- point PHP error_log at the same customer-visible path that
|
|
|
|
|
## cac:phpNN uses for php-fpm errors. Drop-in compat: customer code that
|
|
|
|
|
## was tailing /home/$user/logs/php-fpm/error.log on the old image will
|
|
|
|
|
## see lsphp's PHP errors in the exact same file on the new image.
|
|
|
|
|
## Rendered as a tiny ini in lsphp's scan dir; PHP merges it after the
|
|
|
|
|
## production-tuning overrides at startup.
|
|
|
|
|
SCAN_DIR=$(/usr/local/lsws/lsphp${PHPVER}/bin/lsphp -i 2>/dev/null | awk -F'=> ' '/^Scan this dir/ {print $2; exit}')
|
|
|
|
|
if [ -n "$SCAN_DIR" ]; then
|
|
|
|
|
cat > "$SCAN_DIR/99-user-error-log.ini" <<EOF
|
|
|
|
|
; rendered at container start by entrypoint-litespeed.sh
|
|
|
|
|
error_log = /home/${user}/logs/php-fpm/error.log
|
|
|
|
|
log_errors = On
|
|
|
|
|
EOF
|
2026-06-03 06:21:37 -07:00
|
|
|
|
|
|
|
|
## Per-site opcache override (panel: Advanced Tuning → OpCache size).
|
|
|
|
|
## Falls back to the global lsphp-overrides.ini values (64 MB / 8000 files)
|
|
|
|
|
## when the env vars aren't set. Numeric range/sanity is enforced in the
|
|
|
|
|
## WHP panel before the env var lands here.
|
|
|
|
|
if [ -n "${OPCACHE_MEMORY_MB:-}" ] || [ -n "${OPCACHE_MAX_FILES:-}" ]; then
|
|
|
|
|
{
|
|
|
|
|
echo "; rendered at container start by entrypoint-litespeed.sh"
|
|
|
|
|
echo "; per-site override from WHP whp.sites.opcache_*_override"
|
|
|
|
|
[ -n "${OPCACHE_MEMORY_MB:-}" ] && echo "opcache.memory_consumption = ${OPCACHE_MEMORY_MB}"
|
|
|
|
|
[ -n "${OPCACHE_MAX_FILES:-}" ] && echo "opcache.max_accelerated_files = ${OPCACHE_MAX_FILES}"
|
|
|
|
|
} > "$SCAN_DIR/99-user-opcache.ini"
|
|
|
|
|
fi
|
2026-06-02 10:53:44 -07:00
|
|
|
fi
|
|
|
|
|
|
2026-06-02 20:06:56 -07:00
|
|
|
## ---- ownership: OLS runs as $user end-to-end (server-level user set by
|
|
|
|
|
## create-vhost-litespeed.sh, no setUIDMode). So OLS runtime dirs need to
|
|
|
|
|
## be customer-owned for log writes, swap files, lsphp socket creation.
|
|
|
|
|
## Master still starts as root for port binding, then drops privs to $user.
|
|
|
|
|
chown -R "$user:$user" /usr/local/lsws/logs /usr/local/lsws/conf/cert /tmp/lshttpd 2>/dev/null || true
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
chown -R "$user:$user" "/home/$user"
|
|
|
|
|
chmod 755 "/home/$user"
|
|
|
|
|
|
|
|
|
|
## ---- drop healthz so docker HEALTHCHECK passes before customer files
|
|
|
|
|
## Always rewrite as customer; suexec lsphp will read it as that uid too.
|
|
|
|
|
sudo -u "$user" sh -c "echo ok > /home/$user/public_html/healthz"
|
|
|
|
|
|
|
|
|
|
## ---- DEV: local mariadb + memcached for parity with cac entrypoints ----
|
|
|
|
|
if [ "$environment" = "DEV" ]; then
|
|
|
|
|
echo "Starting Dev Deployment (litespeed)"
|
|
|
|
|
mkdir -p "/home/$user/_db_backups"
|
2026-06-02 08:26:19 -07:00
|
|
|
## mariadb-server + memcached are NOT baked into the image (saves ~500MB
|
|
|
|
|
## on PROD pulls). Install them at runtime, but only once per container —
|
|
|
|
|
## the command -v guard means a restart of an already-bootstrapped
|
|
|
|
|
## container skips the apt step and DEV boot stays ~1.5s like PROD.
|
|
|
|
|
## First-boot in DEV adds ~30-60s for the apt install; acceptable
|
|
|
|
|
## tradeoff per the design spec.
|
|
|
|
|
if ! command -v mysqld >/dev/null 2>&1; then
|
|
|
|
|
echo "DEV first boot: installing mariadb-server + memcached..."
|
|
|
|
|
apt-get update -qq
|
|
|
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
|
|
|
mariadb-server memcached
|
|
|
|
|
apt-get clean
|
|
|
|
|
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
|
|
|
fi
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
mkdir -p /run/mysqld && chown mysql:mysql /run/mysqld
|
|
|
|
|
nohup mysqld --user=mysql &>/dev/null &
|
|
|
|
|
if [ ! -f "/home/$user/mysql_creds" ]; then
|
|
|
|
|
sleep 10
|
|
|
|
|
mysql_user=$(openssl rand -hex 7)
|
|
|
|
|
mysql_password=$(openssl rand -hex 12)
|
|
|
|
|
mysql_db="devdb_$(openssl rand -hex 3)"
|
|
|
|
|
mysql -e "CREATE DATABASE $mysql_db;"
|
|
|
|
|
mysql -e "CREATE USER '$mysql_user'@'localhost' IDENTIFIED BY '$mysql_password';"
|
|
|
|
|
mysql -e "GRANT ALL PRIVILEGES ON *.* TO '$mysql_user'@'localhost' WITH GRANT OPTION;"
|
|
|
|
|
mysql -e "FLUSH PRIVILEGES;"
|
|
|
|
|
{
|
|
|
|
|
echo "MySQL User: $mysql_user"
|
|
|
|
|
echo "MySQL Password: $mysql_password"
|
|
|
|
|
echo "MySQL Database: $mysql_db"
|
|
|
|
|
} > "/home/$user/mysql_creds"
|
|
|
|
|
cat "/home/$user/mysql_creds"
|
|
|
|
|
fi
|
|
|
|
|
/usr/bin/memcached -d -u "$user"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## ---- user crontab ----
|
|
|
|
|
if [ ! -f "/home/$user/crontab" ]; then
|
|
|
|
|
{
|
|
|
|
|
echo "# User crontab for $user"
|
|
|
|
|
echo "# Add your cron jobs here"
|
|
|
|
|
} > "/home/$user/crontab"
|
|
|
|
|
chown "$user:$user" "/home/$user/crontab"
|
|
|
|
|
fi
|
|
|
|
|
crontab -u "$user" "/home/$user/crontab"
|
|
|
|
|
service cron start >/dev/null 2>&1 || /usr/sbin/cron
|
|
|
|
|
|
|
|
|
|
## ---- LSCache plugin (background, non-fatal) ----
|
|
|
|
|
( /scripts/install-lscache-wp.sh "$user" >>/var/log/lscache-install.log 2>&1 || true ) &
|
|
|
|
|
|
cac-litespeed: supervise OLS in daemon mode so self-restarts don't kill PID 1
cac-litespeed containers were dying at random intervals and staying 503 until
manually restarted. Root-caused on whp02 (alsacorp, 2026-06-06): the LiteSpeed
Cache / QUIC.cloud integration refreshes the QUIC.cloud IP allowlist on a
schedule and, when it changes, sends SIGUSR1 → "request a graceful server
restart". The entrypoint ran `openlitespeed -n & wait "$OLS_PID"`, so when the
OLD main PID exited after the zero-downtime handoff, `wait` returned, PID 1
(bash) exited, and the whole container went down. The exit was clean (code 0),
so even a restart policy wouldn't reliably catch it — HAProxy just served 503
until someone ran `docker start`.
Replace the `-n` foreground+wait model with a daemon-mode supervisor: start OLS
via `lswsctrl start` (its native model, where it owns the SIGUSR1 handoff and
keeps listeners bound across generations) and have PID 1 follow `lswsctrl
status`. A graceful self-restart is now invisible here (verified zero-downtime);
PID 1 only relaunches on a genuine crash (no live main), with a 5-in-60s
crash-loop cap that bails out to Docker's restart policy / the site monitor.
SIGTERM still drains and exits cleanly for docker stop / recreate.
Verified on a scratch php85 container: survives `lswsctrl restart`, survives a
raw SIGUSR1 to the main (the exact QUIC.cloud path that used to kill it),
relaunches after `kill -9` of the main, and stops cleanly in ~6s on docker stop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 19:15:25 -07:00
|
|
|
## Stream OLS + customer logs to PID-1 stdout so `docker logs` works. Started
|
|
|
|
|
## once, before the supervisor loop — it follows the files across OLS restarts.
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
touch /usr/local/lsws/logs/error.log /usr/local/lsws/logs/access.log
|
2026-06-02 10:53:44 -07:00
|
|
|
touch "/home/$user/logs/apache/error_log" "/home/$user/logs/apache/access_log"
|
|
|
|
|
touch "/home/$user/logs/php-fpm/error.log"
|
|
|
|
|
chown "$user:$user" "/home/$user/logs/apache/error_log" \
|
|
|
|
|
"/home/$user/logs/apache/access_log" \
|
|
|
|
|
"/home/$user/logs/php-fpm/error.log"
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
tail -F /usr/local/lsws/logs/error.log \
|
|
|
|
|
/usr/local/lsws/logs/access.log \
|
2026-06-02 10:53:44 -07:00
|
|
|
"/home/$user/logs/apache/error_log" \
|
|
|
|
|
"/home/$user/logs/apache/access_log" \
|
|
|
|
|
"/home/$user/logs/php-fpm/error.log" 2>/dev/null &
|
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>
2026-06-02 07:32:47 -07:00
|
|
|
|
cac-litespeed: supervise OLS in daemon mode so self-restarts don't kill PID 1
cac-litespeed containers were dying at random intervals and staying 503 until
manually restarted. Root-caused on whp02 (alsacorp, 2026-06-06): the LiteSpeed
Cache / QUIC.cloud integration refreshes the QUIC.cloud IP allowlist on a
schedule and, when it changes, sends SIGUSR1 → "request a graceful server
restart". The entrypoint ran `openlitespeed -n & wait "$OLS_PID"`, so when the
OLD main PID exited after the zero-downtime handoff, `wait` returned, PID 1
(bash) exited, and the whole container went down. The exit was clean (code 0),
so even a restart policy wouldn't reliably catch it — HAProxy just served 503
until someone ran `docker start`.
Replace the `-n` foreground+wait model with a daemon-mode supervisor: start OLS
via `lswsctrl start` (its native model, where it owns the SIGUSR1 handoff and
keeps listeners bound across generations) and have PID 1 follow `lswsctrl
status`. A graceful self-restart is now invisible here (verified zero-downtime);
PID 1 only relaunches on a genuine crash (no live main), with a 5-in-60s
crash-loop cap that bails out to Docker's restart policy / the site monitor.
SIGTERM still drains and exits cleanly for docker stop / recreate.
Verified on a scratch php85 container: survives `lswsctrl restart`, survives a
raw SIGUSR1 to the main (the exact QUIC.cloud path that used to kill it),
relaunches after `kill -9` of the main, and stops cleanly in ~6s on docker stop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 19:15:25 -07:00
|
|
|
## ---- supervise OLS in DAEMON mode (NOT `openlitespeed -n` + wait) ----
|
|
|
|
|
## OLS performs INTERNAL graceful self-restarts: the LiteSpeed Cache /
|
|
|
|
|
## QUIC.cloud integration refreshes the QUIC.cloud IP allowlist on a schedule
|
|
|
|
|
## and, when it changes, sends SIGUSR1 → "request a graceful server restart".
|
|
|
|
|
## In `-n` foreground mode the OLD main PID exits after the zero-downtime
|
|
|
|
|
## handoff; a bare `wait` on that PID lets bash (PID 1) exit and tears the whole
|
|
|
|
|
## container down. Worse, that exit is *clean*, so `RestartPolicy` doesn't
|
|
|
|
|
## reliably catch it — the container just stops and HAProxy serves 503 until
|
|
|
|
|
## someone manually starts it. (Root-caused on whp02 alsacorp, 2026-06-06.)
|
|
|
|
|
##
|
|
|
|
|
## Daemon mode is OLS's native model: it owns the SIGUSR1 handoff, keeps the
|
|
|
|
|
## listeners bound across generations, and rewrites lshttpd.pid to the new main.
|
|
|
|
|
## PID 1 just FOLLOWS the pidfile — a graceful self-restart is invisible here
|
|
|
|
|
## (zero downtime), and we only ever relaunch on a genuine crash (no live main).
|
|
|
|
|
STOP_REQUESTED=0
|
|
|
|
|
term_handler() {
|
|
|
|
|
STOP_REQUESTED=1
|
|
|
|
|
/usr/local/lsws/bin/lswsctrl stop >/dev/null 2>&1 || true
|
|
|
|
|
}
|
|
|
|
|
trap term_handler TERM INT
|
|
|
|
|
|
|
|
|
|
## Authoritative, path-independent liveness check: `lswsctrl status` prints
|
|
|
|
|
## "litespeed is running with PID N." when up (and "...is not running" when
|
|
|
|
|
## down). We match the running message specifically — a bare grep for "running"
|
|
|
|
|
## would also match "not running". (This image keeps the pidfile under
|
|
|
|
|
## /tmp/lshttpd, not logs/, so we never hard-code a pidfile path.)
|
|
|
|
|
ols_running() { /usr/local/lsws/bin/lswsctrl status 2>/dev/null | grep -qi 'running with pid'; }
|
|
|
|
|
|
|
|
|
|
## Crash-loop cap: if OLS can't stay up, bail out so Docker's restart policy and
|
|
|
|
|
## the site-health monitor escalate instead of us hot-looping forever.
|
|
|
|
|
MAX_STARTS=5
|
|
|
|
|
WINDOW=60
|
|
|
|
|
starts=""
|
|
|
|
|
|
|
|
|
|
start_ols() {
|
|
|
|
|
/usr/local/lsws/bin/lswsctrl start >/dev/null 2>&1 || true
|
|
|
|
|
## wait up to 10s for the daemon to report running
|
|
|
|
|
for _ in $(seq 1 20); do
|
|
|
|
|
ols_running && return 0
|
|
|
|
|
sleep 0.5
|
|
|
|
|
done
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ! start_ols; then
|
|
|
|
|
echo "entrypoint: OLS failed to start (not running after 10s)." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo "entrypoint: OLS started in daemon mode — $(/usr/local/lsws/bin/lswsctrl status 2>/dev/null || true)"
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
if ols_running; then
|
|
|
|
|
sleep 3
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## Not running this instant. This is EITHER a clean shutdown OR the brief
|
|
|
|
|
## handoff window of a graceful self-restart (status momentarily reports down
|
|
|
|
|
## while the new main takes over). Grace, then re-check before judging.
|
|
|
|
|
sleep 2
|
|
|
|
|
if [ "$STOP_REQUESTED" -eq 0 ] && ols_running; then
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ "$STOP_REQUESTED" -eq 1 ]; then
|
|
|
|
|
echo "entrypoint: SIGTERM received, OLS stopped — exiting."
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## Genuine crash: not running and no shutdown requested. Relaunch, capped.
|
|
|
|
|
now=$(date +%s)
|
|
|
|
|
starts="$starts $now"
|
|
|
|
|
pruned=""
|
|
|
|
|
for t in $starts; do
|
|
|
|
|
[ $((now - t)) -lt "$WINDOW" ] && pruned="$pruned $t"
|
|
|
|
|
done
|
|
|
|
|
starts="$pruned"
|
|
|
|
|
n=$(echo $starts | wc -w)
|
|
|
|
|
echo "entrypoint: OLS not running — relaunching (attempt $n/$MAX_STARTS within ${WINDOW}s)." >&2
|
|
|
|
|
if [ "$n" -ge "$MAX_STARTS" ]; then
|
|
|
|
|
echo "entrypoint: OLS crash-looping ($n starts in ${WINDOW}s) — bailing out for Docker restart policy / monitor." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
start_ols || true
|
|
|
|
|
done
|