FrankenPHP Worker 模式下必须手动调高 ulimit -n,因常驻进程持续复用数据库连接、日志句柄等资源,默认 1024 迅速耗尽,导致 Too many open files 等错误;systemd 需在 service 文件中设 LimitNOFILE,Docker/K8s 需运行时传入,且修改后必须重启进程才生效。

FrankenPHP 进程的 ulimit -n 为什么必须手动调高
FrankenPHP Worker 模式下,每个 PHP 进程长期运行,会持续复用数据库连接、日志文件句柄、临时文件、flock 锁文件等资源。系统默认的 ulimit -n(通常是 1024)很快就会被耗尽,表现为 Too many open files 错误、fopen() failed: Too many open files 或 MySQL 报 Connection refused(实际是连接池满,但底层错误常被误读)。这不是 FrankenPHP 的 bug,而是常驻进程对系统资源约束更敏感的自然结果。
systemd 服务里怎么永久生效 ulimit
如果你用 systemctl start frankenphp 启动(推荐方式),必须在 systemd unit 文件中显式设置,仅在 shell 里 ulimit -n 65536 无效——那只会作用于当前终端启动的前台进程,而 daemon 模式由 systemd 独立 fork,继承的是 systemd 的默认限制。
- 编辑服务文件:
sudo systemctl edit frankenphp.service - 填入以下内容(注意缩进必须是两个空格):
[Service] LimitNOFILE=65536 LimitNOFILESoft=65536
- 重载并重启:
sudo systemctl daemon-reload && sudo systemctl restart frankenphp - 验证是否生效:
sudo cat /proc/$(pgrep -f frankenphp)/limits | grep "Max open files",应看到两列都是 65536
容器环境(Docker / Kubernetes)怎么设
容器内 systemd 不可用,ulimit 必须在运行时传入。Docker 需加 --ulimit nofile=65536:65536;Kubernetes 则在 Pod spec 的 securityContext 下配置:
securityContext:
ulimits:
- name: nofile
soft: 65536
hard: 65536。别漏掉 hard 值,否则 PHP 的 setrlimit() 调用可能失败。
PHP 层还能再兜底吗
可以,但只是辅助手段,不能替代系统级设置。在 Worker 入口脚本(如 frankenphp-worker.php)开头加:
if (function_exists('setrlimit')) {
setrlimit(RLIMIT_NOFILE, 65536, 65536);
}。但要注意:该调用需在进程早期执行,且只对当前进程有效;若 PHP 以 www-data 用户运行,而该用户被 PAM 限制了 nofile 上限,则 setrlimit() 会静默失败——所以仍要优先确保 /etc/security/limits.conf 或 systemd 设置到位。
真正容易被忽略的是:ulimit 修改后,必须重启 FrankenPHP 进程(不是 reload),因为已运行的 worker 进程不会动态继承新限制。哪怕改完 systemd 配置,也得 systemctl restart,否则 cat /proc/PID/limits 显示的仍是旧值。



















