walkor/workerman · critical · RuntimeException

Fork fail

Error message

Fork fail

What it means

When starting in daemon mode (-d/--daemon), Worker::daemonize() forks to detach from the terminal. pcntl_fork() returned -1, meaning the OS refused to create the process - typically because a process limit was hit (RLIMIT_NPROC, cgroup pids.max in containers) or memory could not be allocated for the child.

Source

Thrown at src/Worker.php:1438

            SIGUSR2 => 'SIGUSR2',
            SIGIOT => 'SIGIOT',
            SIGIO => 'SIGIO',
            default => $signal,
        };
    }

    /**
     * Run as daemon mode.
     */
    protected static function daemonize(): void
    {
        if (!static::$daemonize || DIRECTORY_SEPARATOR !== '/') {
            return;
        }
        umask(0);
        $pid = pcntl_fork();
        if (-1 === $pid) {
            throw new RuntimeException('Fork fail');
        } elseif ($pid > 0) {
            exit(0);
        }
        if (-1 === posix_setsid()) {
            throw new RuntimeException("Setsid fail");
        }
        // Fork again avoid SVR4 system regain the control of terminal.
        $pid = pcntl_fork();
        if (-1 === $pid) {
            throw new RuntimeException("Fork fail");
        } elseif (0 !== $pid) {
            exit(0);
        }
    }

    /**
     * Redirect standard output to stdoutFile.
     *

View on GitHub (pinned to 1391112a61)

Solutions

  1. Raise the limits: 'ulimit -u 65535' (or the systemd/container equivalent, e.g. docker run --pids-limit=-1 or a higher K8s pid limit)
  2. Count actual usage with 'ps -eLf | wc -l' or the cgroup pids.current file to see what is exhausting the ceiling
  3. As a workaround, run without -d (foreground) to isolate whether the daemonize fork specifically is the blocker, and reduce total worker processes

Example fix

# before
php start.php start -d   # master at nproc/pids limit -> RuntimeException('Fork fail')

# after
ulimit -u 65535            # or raise docker --pids-limit / k8s pod pids limit
php start.php start -d
Defensive patterns

Strategy: validation

Validate before calling

$soft = posix_getrlimit(); // before starting with -d
if (isset($soft['RLIMIT_NPROC']) && $soft['RLIMIT_NPROC'] !== 'unlimited' && $soft['RLIMIT_NPROC'] < 1024) {
    fwrite(STDERR, "RLIMIT_NPROC={$soft['RLIMIT_NPROC']} is low; fork may fail - raise it before daemonizing\n");
}

Prevention

When it happens

Trigger: Running 'php start.php start -d' on a host already at its user process limit; Docker/Kubernetes with a low --pids-limit or pod pids cgroup ceiling; fork attempted while memory overcommit refuses the copy.

Common situations: Shared servers with tight ulimit -u; containers with default pids limits while the app forks many workers; CI environments restricting process creation; systems recovering from a fork-bomb incident with lowered limits.

Related errors


AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21). Data as JSON: /api/errors/7061d86b744d70bf. Report an issue: GitHub.