php / php/frankenphp

PHP requests return 500 during shutdown_delay after SIGTERM

Open
#2,644 1 comment 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.