PHP requests return 500 during shutdown_delay after SIGTERM
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 11.3k
- Forks
- 488
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 11
Description
What happened?
With shutdown_delay 30s, PHP requests return HTTP 500 after SIGTERM while Caddy continues serving static responses. The error is FrankenPHP is not running. This reproduces with an unmodified official image and a trivial PHP script, without a framework or Kubernetes.
Caddy documents that the server continues operating normally during shutdown_delay, before the grace period begins. I expected PHP requests to remain serviceable during that delay, while the health endpoint returns 503.
Reproduced on FrankenPHP 1.12.6 and 1.12.7, PHP 8.5.9, Caddy 2.11.4, linux/arm64, using the Debian Trixie Docker images. The 1.12.7 image digest was sha256:f92d81eb3fe4fd18b35d3d58192b7cc3acc8943817bbf39f2fcf0be02a3916dc.
Reproduction
Save this as Caddyfile:
{
admin localhost:2019
auto_https off
shutdown_delay 30s
grace_period 300s
frankenphp {
num_threads 2
}
}
:8080 {
log
handle /health {
@stopping vars {http.shutting_down} true
respond @stopping "stopping" 503
respond "healthy" 200
}
handle /static {
respond "static-ok" 200
}
handle {
root * /app/public
php_server
}
}
Save this as index.php:
<?php
header('Content-Type: text/plain');
echo "php-ok\n";
Start the container:
container=$(docker run -d -p 127.0.0.1::8080 \
-v "$PWD/Caddyfile:/etc/caddy/Caddyfile:ro" \
-v "$PWD/index.php:/app/public/index.php:ro" \
--entrypoint frankenphp \
dunglas/frankenphp:1.12.7-php8.5.9-trixie \
run --config /etc/caddy/Caddyfile)
endpoint=$(docker port "$container" 8080/tcp)
Once curl -i "http://$endpoint/" returns 200 and php-ok, send SIGTERM and immediately request all three endpoints:
docker kill --signal=TERM "$container"
curl -i "http://$endpoint/"
curl -i "http://$endpoint/static"
curl -i "http://$endpoint/health"
docker logs "$container"
Observed: PHP returns 500, static returns 200, and health returns 503. In the automated 1.12.7 run, PHP returned 79 sampled 500 responses throughout the 30-second delay; each was followed by a successful static request. The container then exited normally. Cleanup: docker rm -f "$container".
If the first shutdown does not reproduce, repeat with a fresh container; the app-stop order can differ between runs.
Suspected cause and local experiment
FrankenPHPApp.Stop() calls frankenphp.Shutdown() when Caddy is exiting, while Caddy stops its apps without dependency ordering. PHP can therefore stop before the HTTP app finishes its shutdown delay.
I tested moving PHP shutdown to a single process-global caddy.OnExit callback. On both releases, the local patch eliminated these 500s and the process exited cleanly. A 40-second PHP request already running at SIGTERM also completed successfully across the 30-second delay, including after two configuration reloads. That test explicitly allowed 90 seconds of PHP execution time. Worker-mode behavior was not tested.
Is deferring PHP shutdown until after the HTTP app has stopped the appropriate fix here?
Build Type
Docker (Debian Trixie)
Worker Mode
No
Operating System / CPU Architecture
GNU/Linux container on macOS/OrbStack; aarch64.
PHP configuration
Official image defaults, with no custom PHP configuration or additional extensions for the minimal reproduction.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with FrankenPHPApp.Stop(), frankenphp.Shutdown(), and the proposed caddy.OnExit experiment to trace shutdown ordering. Run the supplied Docker reproduction and verify that PHP remains available during shutdown_delay, the health endpoint returns 503, static responses still succeed, and the container exits cleanly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, go, php
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 56/100