## cac-litespeed — OpenLiteSpeed customer container, LSAPI-native. ## ## Built on top of the LiteSpeed-maintained prebuilt image rather than ## installed-from-RPM on AlmaLinux 10. Rationale: ## - The EL10 RPM ships an empty /usr/local/lsws/cgid/ directory (the ## lscgid suexec helper is built by the upstream tarball install.sh, ## not packaged), which makes LSAPI unusable. ## - The prebuilt image is Ubuntu 24.04-based and includes lsphp + ## everything WP/WooCommerce needs out of the box (memcached, redis, ## imagick, mbstring, mysqlnd, intl, gd, soap, bcmath, gmp, sodium, ## opcache, ...) — saves us a dozen explicit installs and avoids the ## libonig.so.105 packaging bug entirely. ## - LiteSpeed Inc maintains it; OLS upgrades become a base-image bump. ## ## Tradeoff vs the rest of the CAC family: this image is Ubuntu-based, ## not AlmaLinux. The "cac" naming is now slightly misleading (it's no ## longer Cloud *Apache* Container, it's Cloud LiteSpeed Container) but ## the panel doesn't care and the customer-facing contract is identical. ## ARG before FROM is special — it can be used in the FROM line, but the ## value goes out of scope inside the image, so we re-declare ARG PHPVER ## after FROM for any RUN steps that need it. ARG OLS_VERSION=1.8.5 ARG PHPVER=83 ## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256: of the ## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same ## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can ## only serve content-addressed blobs. Local builds use the tag. Every stage below ## (incl. the emergency lsphp-src stage) starts from this one image. ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} ## ---- EMERGENCY ONLY: lsphp compiled from php.net source ---------------------- ## Idle unless PHP_SRC_VERSION is set (then /lsphp-src-out stays empty and the ## overlay step below is a no-op). Same stage as Dockerfile.lsphp — see there and ## docs/lsphp-source-emergency.md. FROM ${BASE_IMAGE} AS lsphp-src ARG PHPVER=83 ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= ARG EXPECT_PHP_VERSION= ARG PHP_SRC_VERSION= ARG PHP_SRC_SHA256= RUN --mount=type=bind,source=scripts,target=/mnt/scripts \ --mount=type=bind,source=keys/php-src,target=/usr/local/share/php-src-keys \ set -e; \ if [ -n "$PHP_SRC_VERSION" ]; then \ install -m 0755 /mnt/scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh; \ fi; \ PHPVER="${PHPVER}" bash /mnt/scripts/lsphp-src-build.sh FROM ${BASE_IMAGE} ARG PHPVER=83 ENV PHPVER=${PHPVER} ## Tooling we layer on top of the base: ## - gettext-base: envsubst for runtime template rendering ## - sudo: install-lscache-wp.sh runs wp-cli as the customer user ## - composer: not in the base image (wp-cli is) ## - cron: customer crontab support (mirror cac:phpXX behaviour) ## - lsphp83-ldap: not in base image, useful for some WP plugins ## ## NOTE: mariadb-server + memcached were previously installed here for ## DEV-mode parity but bloated the PROD image by ~500MB. They are now ## installed at runtime by entrypoint-litespeed.sh ONLY when ## environment=DEV, mirroring the cac:phpNN pattern. The mysql CLI ## client (used by the DEV creds-bootstrap and by wp-cli) is already ## present in the litespeedtech/openlitespeed base via the mysql-client ## package, so no client-side install is needed at build time. ## ## All apt cache is cleaned in the same layer to keep image size down. ## lsphp${PHPVER}-ldap is the only extra ext we add (everything else WP needs ## ships in the prebuilt base). lsphp84 + lsphp85 don't ship imap or pspell ## from LiteSpeed — customers needing imap should pin to 8.3 or earlier. ## ## SECURITY UPDATES AT BUILD TIME: lsphp-apt-upgrade.sh also upgrades the OS and ## sweeps EVERY installed lsphp${PHPVER}* package to the LiteSpeed repo's current ## release, failing the build if the repo is unreachable — so a rebuild ships a ## PHP security fix as soon as LiteSpeed publishes the .deb, without waiting for ## a new litespeedtech/openlitespeed base tag. See the script header. ## ## CACHE_BUST: CI passes a per-run value so this layer can never be a cache hit ## (an ARG's first use in a RUN is part of that layer's cache key). Local builds: ## pass --build-arg CACHE_BUST=$(date +%s) or use --no-cache, or you may get the ## lsphp from your last build. EXPECT_* are resolved by CI from the repo index ## and asserted here; the same values become the image labels below. ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= ARG EXPECT_PHP_VERSION= ## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 ## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu + ## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no ## layer keeps the cache address. See scripts/pkg-cache-repos.sh. ARG PKG_CACHE= COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/ COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh RUN /usr/local/sbin/pkg-cache-repos.sh apt && \ /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ gettext-base sudo cron \ ca-certificates curl wget \ lsphp${PHPVER}-ldap && \ /usr/local/sbin/pkg-cache-repos.sh restore && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* ## EMERGENCY PATH: overlay the php.net lsphp (no-op on normal builds) and prove ## it has the same extension/feature surface as the packaged one just installed ## — see scripts/lsphp-src-overlay.sh. Bind mounts: no extra image layer content. ARG PHP_SRC_VERSION= ARG LSPHP_SOURCE=litespeed-apt RUN --mount=type=bind,from=lsphp-src,source=/lsphp-src-out,target=/lsphp-src-out,rw \ --mount=type=bind,source=scripts,target=/mnt/scripts \ install -m 0644 /mnt/scripts/lsphp-src-probe.php /usr/local/share/lsphp-src-probe.php && \ bash /mnt/scripts/lsphp-src-overlay.sh runtime "${PHPVER}" && \ rm -f /usr/local/share/lsphp-src-probe.php ## Composer (matches the wp-cli pattern in the existing CAC Dockerfile — ## phar download, no `php install` pipe since lsphp's CLI mode is fine). RUN curl -fsSL -o /usr/local/bin/composer https://getcomposer.org/download/latest-stable/composer.phar && \ chmod +x /usr/local/bin/composer ## Our scripts + config templates layer in last (they change most often, ## keep them off the slow apt layer). COPY ./scripts/entrypoint-litespeed.sh \ ./scripts/create-vhost-litespeed.sh \ ./scripts/detect-memory-litespeed.sh \ ./scripts/install-lscache-wp.sh \ ./scripts/log-rotate.sh \ /scripts/ RUN chmod +x /scripts/* COPY ./configs/litespeed/ /etc/lsws-templates/ ## php2shell mitigation (CVE-2026-109618/-109619). Named zz- so it sorts after ## every other scan-dir ini (incl. runtime-written 99-*) and wins. Remove once ## all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12. COPY ./configs/zz-php2shell-mitigation.ini /etc/lsws-templates/zz-php2shell-mitigation.ini ## Apply our production lsphp ini overrides. Ask lsphp for its scan dir ## directly (varies by PHP minor version: 8.3/8.4/8.5 each have their own ## /usr/local/lsws/lsphpNN/etc/php/8.M/mods-available/). Dockerfile RUN uses ## /bin/sh so we explicitly `bash -c` for safer scripting. RUN bash -c 'set -e; \ SCAN_DIR=$(/usr/local/lsws/lsphp${PHPVER}/bin/lsphp -i 2>/dev/null | awk -F"=> " "/^Scan this dir/ {print \$2; exit}"); \ mkdir -p "$SCAN_DIR"; \ cp /etc/lsws-templates/lsphp-overrides.ini "$SCAN_DIR/99-prod-overrides.ini"; \ cp /etc/lsws-templates/zz-php2shell-mitigation.ini "$SCAN_DIR/zz-php2shell-mitigation.ini"; \ echo "wrote overrides to $SCAN_DIR"' ## Disable the OLS WebAdmin port for customer-facing containers. Bind admin ## listener to loopback so it's unreachable even from the docker network. RUN sed -i 's|^[[:space:]]*address[[:space:]]\+\*:| address 127.0.0.1:|' \ /usr/local/lsws/admin/conf/admin_config.conf 2>/dev/null || true ## Cron entry for log rotation (mirrors cac:phpXX). RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab ## Inspect without running the image: ## docker buildx imagetools inspect --format '{{json .Image.Config.Labels}}' ## Values come from CI's repo-index lookup and were asserted against the ## installed packages by lsphp-apt-upgrade.sh above ("unverified" = local build). ## Emergency (php.net) builds: see the same LABEL block in Dockerfile.lsphp. LABEL org.whp.php-version="${PHP_SRC_VERSION:-${EXPECT_PHP_VERSION:-unverified}}" \ org.whp.lsphp-package-version="${EXPECT_LSPHP_PKG:-unverified}" \ org.whp.lsphp-source="${LSPHP_SOURCE}" \ org.whp.php-flavour="lsphp${PHPVER}" EXPOSE 80 443 ## Healthcheck: the entrypoint drops a static /healthz into the customer ## docroot at boot, so this passes even before any customer files exist. HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD curl -fsSk https://127.0.0.1/healthz || exit 1 ENTRYPOINT ["/scripts/entrypoint-litespeed.sh"]