Skip to content

Pin Caddy to the version FrankenPHP's go.mod declares - #713

Merged
jaydrogers merged 2 commits into
release/webserver-improvements-and-fixesfrom
fix/frankenphp-pin-caddy-version
Oct 3, 2026
Merged

jaydrogers merged 2 commits into
release/webserver-improvements-and-fixesfrom
fix/frankenphp-pin-caddy-version

Conversation

@jaydrogers

@jaydrogers jaydrogers commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Fixes #712

v5.0.0-beta3 FrankenPHP images segfault the worker on HTTP/2 as soon as PHP keeps running after fastcgi_finish_request(). Symfony's Response::send() calls it and Laravel runs terminate callbacks afterward, so every request kills the container. That covers Caddy serving browsers directly in SSL_MODE=full and Traefik proxying to port 8443, which uses HTTP/2 to the backend.

Cause

xcaddy build was called with no version, so xcaddy resolved the latest Caddy release at build time and overrode the pin in caddy/go.mod at the FRANKENPHP_VERSION tag. FrankenPHP v1.12.7 declares caddyserver/caddy/v2 v2.11.4; beta2 happened to build against 2.11.4 and beta3 picked up 2.11.6. Nothing in this repo changed between the two betas to cause it, which is the other half of the problem: FrankenPHP builds were not reproducible.

Upstream is unaffected because its own Dockerfile builds with go install from that go.mod, so dunglas/frankenphp:1.12.7-php8.4 ships Caddy 2.11.4.

Changes

Pin Caddy. A CADDY_VERSION build arg set to v2.11.4 is passed to xcaddy build, the same way the plugin versions are already pinned to match caddy/go.mod. Bumping Caddy is now a deliberate edit rather than whatever the build happens to resolve.

Add the missing check. scripts/test-image.sh never had a request that finished before PHP did, which is why this reached beta3. The new check serves a script that calls fastcgi_finish_request() and then keeps working, over both HTTP/1.1 and HTTP/2, and fails if the web server stops. It runs against its own container with SSL_MODE=full, because HTTP/2 needs TLS and that mode stops serving the plain HTTP port. It reads NGINX_HTTPS_PORT, APACHE_HTTPS_PORT or CADDY_HTTPS_PORT, so it covers all three web variations and skips images without a web server.

Containers no longer start with --rm. cleanup() already removes everything the script starts, while --rm discarded the logs of a container that crashed, which is exactly when they are worth reading. With it gone, a failure now prints the panic.

Verification

Repro: public/index.php with echo "ok"; fastcgi_finish_request(); usleep(10000);, run with SSL_MODE=full, one curl -k --http2 https://localhost:8443/.

Build Caddy PHP HTTP/1.1 HTTP/2
8.4-frankenphp-v5.0.0-beta2 2.11.4 8.4.25 OK OK
8.4-frankenphp-v5.0.0-beta3 2.11.6 8.4.26 OK crash, exit 2
release branch before this change 2.11.6 8.4.26 OK crash 3/3
this branch, bookworm 2.11.4 8.4.26 OK OK 3/3
this branch, alpine3.24 2.11.4 8.4.26 OK OK 3/3

PHP is ruled out: dropping the official dunglas/frankenphp:1.12.7-php8.4-bookworm binary (same FrankenPHP, same PHP 8.4.26, Caddy 2.11.4) into the unmodified beta3 image stops the crash, 3/3.

The new check was confirmed against two builds of this branch that differ only by --build-arg CADDY_VERSION:

  • v2.11.6: fails on ❌ Web server stopped after a http2 request finished before PHP did (status: exited), after the HTTP/1.1 pass, and prints the panic. Exit 1.
  • v2.11.4: all 15 checks pass.

Full suite passes on 8.4-frankenphp-bookworm, 8.4-frankenphp-alpine3.24, 8.4-fpm-nginx-bookworm, 8.4-fpm-apache-bookworm and 8.4-fpm-bookworm (which skips the new check, having no web server). NGINX, Apache and FrankenPHP were each confirmed to really negotiate HTTP/2 rather than quietly falling back, with curl --http2 -w '%{http_version}'.

hadolint v2.15.1 and shellcheck v0.11.0 at warning severity are both clean, matching what service_lint.yml runs.

Follow-up, not in this PR

The underlying regression is in Caddy between 2.11.4 and 2.11.6 and should be reported upstream, otherwise a future deliberate bump reintroduces it. The new check in this PR turns that into a failed build rather than a broken release. The 121-commit range includes the Go 1.26 dependency floor bump, which moves x/net/http2, and caddyhttp: surface write timeout errors in access log.

xcaddy build with no version resolves the latest Caddy release, which
overrides the pin in caddy/go.mod at the FRANKENPHP_VERSION tag. Caddy
2.11.6 segfaults the worker on HTTP/2 when PHP keeps running after
fastcgi_finish_request(), which takes down every Laravel app behind
HTTP/2 or a Traefik backend. Fixes #712
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Images for PR #713

Build Test Publish
✅ 79 of 79 images built ✅ Every image passed on amd64 and arm64 ✅ Published to serversideup/php-dev

Try it:

docker run --rm -v "$PWD:/var/www/html" -p 8080:8080 serversideup/php-dev:713-8.5-fpm-nginx

Every image is on Docker Hub as serversideup/php-dev:713-<php>-<variation>-<os>. Browse all tags or view the run.

All images with sizes

Sizes are compressed, per architecture.

Variation PHP Base OS amd64 arm64 Image
cli 8.5.11 alpine3.23 50.5 MB 50.4 MB serversideup/php-dev:713-8.5.11-cli-alpine3.23
cli 8.5.11 alpine3.24 50.6 MB 50.4 MB serversideup/php-dev:713-8.5.11-cli-alpine3.24
cli 8.5.11 bookworm 195.8 MB 188.5 MB serversideup/php-dev:713-8.5.11-cli-bookworm
cli 8.5.11 trixie 195.7 MB 187.9 MB serversideup/php-dev:713-8.5.11-cli-trixie
cli 8.4.26 alpine3.23 47.4 MB 47.5 MB serversideup/php-dev:713-8.4.26-cli-alpine3.23
cli 8.4.26 alpine3.24 47.5 MB 47.6 MB serversideup/php-dev:713-8.4.26-cli-alpine3.24
cli 8.4.26 bookworm 191.9 MB 184.9 MB serversideup/php-dev:713-8.4.26-cli-bookworm
cli 8.4.26 trixie 191.8 MB 184.3 MB serversideup/php-dev:713-8.4.26-cli-trixie
cli 8.3.35 alpine3.23 43.4 MB 43.9 MB serversideup/php-dev:713-8.3.35-cli-alpine3.23
cli 8.3.35 alpine3.24 43.5 MB 44.0 MB serversideup/php-dev:713-8.3.35-cli-alpine3.24
cli 8.3.35 bookworm 184.2 MB 177.8 MB serversideup/php-dev:713-8.3.35-cli-bookworm
cli 8.3.35 trixie 187.0 MB 179.8 MB serversideup/php-dev:713-8.3.35-cli-trixie
cli 8.2.34 alpine3.23 42.6 MB 43.1 MB serversideup/php-dev:713-8.2.34-cli-alpine3.23
cli 8.2.34 alpine3.24 42.7 MB 43.2 MB serversideup/php-dev:713-8.2.34-cli-alpine3.24
cli 8.2.34 bookworm 186.2 MB 179.5 MB serversideup/php-dev:713-8.2.34-cli-bookworm
cli 8.2.34 trixie 186.1 MB 179.0 MB serversideup/php-dev:713-8.2.34-cli-trixie
cli 8.1.34 alpine3.22 42.4 MB 43.0 MB serversideup/php-dev:713-8.1.34-cli-alpine3.22
cli 8.1.34 bookworm 185.4 MB 178.7 MB serversideup/php-dev:713-8.1.34-cli-bookworm
cli 8.1.34 trixie 185.2 MB 178.1 MB serversideup/php-dev:713-8.1.34-cli-trixie
fpm 8.5.11 alpine3.23 44.6 MB 44.7 MB serversideup/php-dev:713-8.5.11-fpm-alpine3.23
fpm 8.5.11 alpine3.24 44.6 MB 44.8 MB serversideup/php-dev:713-8.5.11-fpm-alpine3.24
fpm 8.5.11 bookworm 184.1 MB 177.1 MB serversideup/php-dev:713-8.5.11-fpm-bookworm
fpm 8.5.11 trixie 184.0 MB 176.5 MB serversideup/php-dev:713-8.5.11-fpm-trixie
fpm 8.4.26 alpine3.23 42.4 MB 42.7 MB serversideup/php-dev:713-8.4.26-fpm-alpine3.23
fpm 8.4.26 alpine3.24 42.5 MB 42.7 MB serversideup/php-dev:713-8.4.26-fpm-alpine3.24
fpm 8.4.26 bookworm 182.0 MB 175.1 MB serversideup/php-dev:713-8.4.26-fpm-bookworm
fpm 8.4.26 trixie 181.8 MB 174.5 MB serversideup/php-dev:713-8.4.26-fpm-trixie
fpm 8.3.35 alpine3.23 39.3 MB 39.9 MB serversideup/php-dev:713-8.3.35-fpm-alpine3.23
fpm 8.3.35 alpine3.24 39.3 MB 40.0 MB serversideup/php-dev:713-8.3.35-fpm-alpine3.24
fpm 8.3.35 bookworm 176.0 MB 169.7 MB serversideup/php-dev:713-8.3.35-fpm-bookworm
fpm 8.3.35 trixie 178.7 MB 171.8 MB serversideup/php-dev:713-8.3.35-fpm-trixie
fpm 8.2.34 alpine3.23 38.6 MB 39.2 MB serversideup/php-dev:713-8.2.34-fpm-alpine3.23
fpm 8.2.34 alpine3.24 38.6 MB 39.3 MB serversideup/php-dev:713-8.2.34-fpm-alpine3.24
fpm 8.2.34 bookworm 178.1 MB 171.7 MB serversideup/php-dev:713-8.2.34-fpm-bookworm
fpm 8.2.34 trixie 178.0 MB 171.1 MB serversideup/php-dev:713-8.2.34-fpm-trixie
fpm 8.1.34 alpine3.22 38.5 MB 39.2 MB serversideup/php-dev:713-8.1.34-fpm-alpine3.22
fpm 8.1.34 bookworm 177.6 MB 171.1 MB serversideup/php-dev:713-8.1.34-fpm-bookworm
fpm 8.1.34 trixie 177.4 MB 170.4 MB serversideup/php-dev:713-8.1.34-fpm-trixie
fpm-apache 8.5.11 bookworm 190.4 MB 183.9 MB serversideup/php-dev:713-8.5.11-fpm-apache-bookworm
fpm-apache 8.5.11 trixie 190.4 MB 183.4 MB serversideup/php-dev:713-8.5.11-fpm-apache-trixie
fpm-apache 8.4.26 bookworm 188.3 MB 181.9 MB serversideup/php-dev:713-8.4.26-fpm-apache-bookworm
fpm-apache 8.4.26 trixie 188.2 MB 181.4 MB serversideup/php-dev:713-8.4.26-fpm-apache-trixie
fpm-apache 8.3.35 bookworm 182.3 MB 176.5 MB serversideup/php-dev:713-8.3.35-fpm-apache-bookworm
fpm-apache 8.3.35 trixie 185.1 MB 178.6 MB serversideup/php-dev:713-8.3.35-fpm-apache-trixie
fpm-apache 8.2.34 bookworm 184.5 MB 178.5 MB serversideup/php-dev:713-8.2.34-fpm-apache-bookworm
fpm-apache 8.2.34 trixie 184.4 MB 178.0 MB serversideup/php-dev:713-8.2.34-fpm-apache-trixie
fpm-apache 8.1.34 bookworm 183.9 MB 177.9 MB serversideup/php-dev:713-8.1.34-fpm-apache-bookworm
fpm-apache 8.1.34 trixie 183.8 MB 177.3 MB serversideup/php-dev:713-8.1.34-fpm-apache-trixie
fpm-nginx 8.5.11 alpine3.23 50.7 MB 51.2 MB serversideup/php-dev:713-8.5.11-fpm-nginx-alpine3.23
fpm-nginx 8.5.11 alpine3.24 52.1 MB 52.7 MB serversideup/php-dev:713-8.5.11-fpm-nginx-alpine3.24
fpm-nginx 8.5.11 bookworm 191.9 MB 185.3 MB serversideup/php-dev:713-8.5.11-fpm-nginx-bookworm
fpm-nginx 8.5.11 trixie 198.7 MB 191.5 MB serversideup/php-dev:713-8.5.11-fpm-nginx-trixie
fpm-nginx 8.4.26 alpine3.23 48.5 MB 49.2 MB serversideup/php-dev:713-8.4.26-fpm-nginx-alpine3.23
fpm-nginx 8.4.26 alpine3.24 49.9 MB 50.6 MB serversideup/php-dev:713-8.4.26-fpm-nginx-alpine3.24
fpm-nginx 8.4.26 bookworm 189.7 MB 183.3 MB serversideup/php-dev:713-8.4.26-fpm-nginx-bookworm
fpm-nginx 8.4.26 trixie 196.6 MB 189.5 MB serversideup/php-dev:713-8.4.26-fpm-nginx-trixie
fpm-nginx 8.3.35 alpine3.23 45.4 MB 46.4 MB serversideup/php-dev:713-8.3.35-fpm-nginx-alpine3.23
fpm-nginx 8.3.35 alpine3.24 46.8 MB 47.9 MB serversideup/php-dev:713-8.3.35-fpm-nginx-alpine3.24
fpm-nginx 8.3.35 bookworm 183.8 MB 177.9 MB serversideup/php-dev:713-8.3.35-fpm-nginx-bookworm
fpm-nginx 8.3.35 trixie 193.5 MB 186.7 MB serversideup/php-dev:713-8.3.35-fpm-nginx-trixie
fpm-nginx 8.2.34 alpine3.23 44.7 MB 45.7 MB serversideup/php-dev:713-8.2.34-fpm-nginx-alpine3.23
fpm-nginx 8.2.34 alpine3.24 46.1 MB 47.2 MB serversideup/php-dev:713-8.2.34-fpm-nginx-alpine3.24
fpm-nginx 8.2.34 bookworm 185.9 MB 179.8 MB serversideup/php-dev:713-8.2.34-fpm-nginx-bookworm
fpm-nginx 8.2.34 trixie 192.8 MB 186.1 MB serversideup/php-dev:713-8.2.34-fpm-nginx-trixie
fpm-nginx 8.1.34 alpine3.22 44.6 MB 45.8 MB serversideup/php-dev:713-8.1.34-fpm-nginx-alpine3.22
fpm-nginx 8.1.34 bookworm 186.5 MB 180.3 MB serversideup/php-dev:713-8.1.34-fpm-nginx-bookworm
fpm-nginx 8.1.34 trixie 194.0 MB 187.2 MB serversideup/php-dev:713-8.1.34-fpm-nginx-trixie
frankenphp 8.5.11 alpine3.23 78.7 MB 76.5 MB serversideup/php-dev:713-8.5.11-frankenphp-alpine3.23
frankenphp 8.5.11 alpine3.24 78.7 MB 76.5 MB serversideup/php-dev:713-8.5.11-frankenphp-alpine3.24
frankenphp 8.5.11 bookworm 217.0 MB 207.9 MB serversideup/php-dev:713-8.5.11-frankenphp-bookworm
frankenphp 8.5.11 trixie 216.8 MB 207.3 MB serversideup/php-dev:713-8.5.11-frankenphp-trixie
frankenphp 8.4.26 alpine3.23 74.7 MB 72.7 MB serversideup/php-dev:713-8.4.26-frankenphp-alpine3.23
frankenphp 8.4.26 alpine3.24 74.7 MB 72.8 MB serversideup/php-dev:713-8.4.26-frankenphp-alpine3.24
frankenphp 8.4.26 bookworm 213.1 MB 204.2 MB serversideup/php-dev:713-8.4.26-frankenphp-bookworm
frankenphp 8.4.26 trixie 212.9 MB 203.6 MB serversideup/php-dev:713-8.4.26-frankenphp-trixie
frankenphp 8.3.35 alpine3.23 69.8 MB 68.3 MB serversideup/php-dev:713-8.3.35-frankenphp-alpine3.23
frankenphp 8.3.35 alpine3.24 69.9 MB 68.4 MB serversideup/php-dev:713-8.3.35-frankenphp-alpine3.24
frankenphp 8.3.35 bookworm 205.3 MB 197.1 MB serversideup/php-dev:713-8.3.35-frankenphp-bookworm
frankenphp 8.3.35 trixie 208.1 MB 199.2 MB serversideup/php-dev:713-8.3.35-frankenphp-trixie

Updated on every push to this PR.

Nothing in the suite called fastcgi_finish_request() and then kept working,
so the Caddy 2.11.6 crash reached beta3 unnoticed. The check runs against
its own container with SSL_MODE=full, since HTTP/2 needs TLS and that mode
stops serving the plain HTTP port, and it covers NGINX, Apache and
FrankenPHP through their *_HTTPS_PORT variables.

Containers no longer start with --rm. cleanup() already removes everything
the script starts, while --rm discarded the logs of a container that
crashed, which is exactly when they are worth reading.
@jaydrogers
jaydrogers merged commit cd5b8a1 into release/webserver-improvements-and-fixes Oct 3, 2026
131 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant