Environment
- FrankenPHP version: 1.12.1
- PHP version: 8.4
- OS: Linux (Debian bookworm, Docker container)
- Caddy config:
num_threads 2, max_threads 8
Description
When pcntl_fork() is called inside a FrankenPHP worker thread (during an HTTP request),
the child process becomes a full copy of the FrankenPHP Go runtime and never terminates —
even after calling exit(0) from PHP.
Each request that calls pcntl_fork() leaves one additional frankenphp process
in S (sleeping) state permanently. These processes accumulate indefinitely.
Reproduction
Minimal controller:
$pid = pcntl_fork();
if ($pid === 0) {
usleep(100); // 100 microseconds
exit(0);
}
Steps:
1. Run FrankenPHP with num_threads 2, max_threads 8
2. Hit the endpoint 20 times
3. Check ps aux inside the container
Before (20 requests):
3 processes
After:
23 processes — all `frankenphp run` with status `S`, never exit
Root cause (hypothesis)
pcntl_fork() in a Go-based runtime duplicates the entire Go runtime — goroutines,
mutexes, thread pools. The child process inherits locks held by threads that no
longer exist in the child, causing the Go runtime to deadlock on shutdown.
PHP's exit(0) is called but the Go process cannot clean up and terminate.
Impact
In production under load, these zombie-like sleeping processes accumulate rapidly:
- PID exhaustion
Notes
- tini as PID 1 does not help — processes are in S state, not Z (zombie),
so tini has nothing to reap
- Process::run() (Symfony/Laravel, uses proc_open) does not reproduce the issue
- Only direct pcntl_fork() call reproduces it
Question
Is pcntl_fork() expected to be unsupported inside FrankenPHP worker threads?
Should FrankenPHP disable pcntl_fork or emit a warning when called in worker context?
---
Environment
num_threads 2,max_threads 8Description
When
pcntl_fork()is called inside a FrankenPHP worker thread (during an HTTP request),the child process becomes a full copy of the FrankenPHP Go runtime and never terminates —
even after calling
exit(0)from PHP.Each request that calls
pcntl_fork()leaves one additionalfrankenphpprocessin
S(sleeping) state permanently. These processes accumulate indefinitely.Reproduction
Minimal controller: