FrankenPHP 更新时端口冲突本质是旧实例未退出导致新实例 bind 失败,需用 lsof(macOS/Linux)或 netstat+tasklist(Windows)精准识别残留进程,确认为 frankenphp 后用 kill -15 优雅终止,并删除 /tmp/frankenphp.sock 避免 socket 冲突。

FrankenPHP 更新服务时新旧实例端口冲突,本质是旧进程没退出,新实例启动时 bind 失败——它不会自动抢占、重试或 fallback,直接报 bind: address already in use。必须手动清理旧实例或隔离端口。
怎么确认是 FrankenPHP 自己残留占了端口
别猜,直接查。旧实例常因 Ctrl+Z 挂起、IDE 终端意外关闭、systemd 未配置 Restart=always 等原因静默存活:
- macOS/Linux:运行
lsof -P -n -i :8080(把8080换成你实际用的端口),看COMMAND列是否为frankenphp或php,再用ps -p PID -o args=确认命令行里有没有frankenphp serve - Windows:管理员运行
netstat -ano | findstr :8080,拿到 PID 后执行tasklist | findstr "1234"(1234 是 PID),若进程名为frankenphp.exe就是它自己没退干净 - 别信
ps aux | grep franken这种模糊匹配——可能匹配到日志里的字符串,误判
安全终止旧 FrankenPHP 实例的实操要点
终止前先区分:是用户可控进程,还是系统级监听(比如 PID=4 的 HTTP.sys)。FrankenPHP 自身进程一定可杀,但得发对信号:
- 优先用
kill -15 PID(SIGTERM):给它机会优雅关闭 socket、释放/tmp/frankenphp.sock、刷完 OPcache 缓存 - 若 10 秒后
lsof还显示该 PID,再用kill -9 PID(SIGKILL)强杀 - Windows 上用
taskkill /f /pid 1234,但注意:如果 PID 是 4,绝对不要 kill —— 那是 HTTP.sys 内核驱动,杀它会断网 - Docker 场景下,别只
docker stop,要确认容器真正退出:docker ps -a | grep franken,有Exited状态才安全
更新部署时如何避免端口冲突(推荐流程)
靠“先停后启”容易出竞态——旧进程还没完全释放端口,新实例已开始 bind。更稳的做法是加隔离层:
立即学习“PHP免费学习笔记(深入)”;
- 启动新实例时强制换端口:
frankenphp serve --addr :8081,等它健康检查通过(curl -I http://localhost:8081/health),再发信号停旧实例(kill -15旧 PID) - 用反向代理做平滑切换:Caddy 或 Nginx 把流量先切到新端口,确认无错误日志后再关旧端口服务
- 如果是 systemd 管理,写好
ExecStop=/bin/kill -15 $MAINPID和RestartSec=2,避免 stop 超时导致 kill -9 强杀 - 别依赖
frankenphp webserver(旧命令)——它已弃用,行为不稳定,统一用frankenphp serve
最易被忽略的一点:FrankenPHP 默认复用 Unix socket(如 /tmp/frankenphp.sock),更新时即使换了 HTTP 端口,这个 socket 文件若没删干净,新实例仍会因地址冲突失败。每次更新前加一行 rm -f /tmp/frankenphp.sock,比查端口还关键。



















