Composer 报错“Too many open files”或卡在 install/update 阶段,主因是系统文件描述符限制过低;需通过 ulimit(macOS/Linux 临时)、launchd plist(macOS 永久)或 systemd user.conf(Linux/WSL2)提升限制,并排查 PHP 的 open_basedir、max_execution_time 及 proc_open() 是否被禁用。

Composer 报错 Too many open files 或卡在 install/update 阶段没反应,大概率是系统对单个进程能打开的文件描述符(file descriptor)数量限制太低,而 Composer 在解压、扫描、加载大量包时会密集打开文件(尤其是 vendor 下成千上万的小文件),触发了限制。
查看当前系统的文件句柄限制
先确认是不是真被限制了,而不是网络或权限问题:
运行 ulimit -n 查看当前 shell 会话的软限制;ulimit -Hn 看硬限制。macOS 默认常为 256 或 1024,Linux 桌面环境常见 1024,而 Composer 中等规模项目轻松需要 4000+ 句柄。
临时提升可运行:ulimit -n 65536(仅当前终端有效)。但别直接写进 ~/.zshrc 全局设太高——某些老旧系统或容器环境可能不支持,反而导致终端启动失败。
macOS 上永久提高 ulimit(绕过 launchd 限制)
macOS 10.15+ 用 launchd 管理进程,ulimit 在 shell 里设了也没法继承给 PHP 进程(Composer 是 PHP 脚本)。必须改系统级配置:
- 创建
/Library/LaunchDaemons/limit.maxfiles.plist,内容含softlimit和hardlimit字段,值设为65536 - 执行
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist - 重启终端(不是仅新开 tab),再跑
ulimit -n验证是否生效
注意:如果用了 Oh My Zsh 或其他 shell 插件,它们可能覆盖 ulimit 设置,建议在 ~/.zshrc 底部加 ulimit -n 65536 作为 fallback。
Linux 下调整 systemd 用户服务限制(常见于 Ubuntu 22.04+/WSL2)
systemd 不读 /etc/security/limits.conf 对用户 session 的设置,尤其 WSL2 默认更严格:
- 编辑
/etc/systemd/user.conf和/etc/systemd/system.conf,取消注释并修改:DefaultLimitNOFILE=65536 - 执行
sudo systemctl daemon-reload - 重启用户 session:
loginctl terminate-user $USER(或直接重启 WSL2:wsl --shutdown) - 重新登录后验证:
systemctl --user show --property=LimitNOFILE
别漏掉 --user 参数——Composer 通常以普通用户运行,不是系统服务。
PHP 自身的 open_basedir 或资源限制干扰
即使系统句柄够了,PHP 层也可能拦住:
- 检查
php -i | grep open_basedir,若返回非no value,且路径没包含vendor/或临时目录,Composer 会因无法访问文件报错(现象类似句柄不足) - 确认
php.ini中max_execution_time没设成 0 以外的极小值(如 30),否则大项目 update 直接超时中断,看起来像卡死 - 某些共享主机禁用
proc_open(),Composer 依赖它调用git、unzip等命令——此时错误是proc_open(): fork failed,和句柄无关,需换用composer install --no-scripts绕过
真正棘手的是:句柄限制 + WSL2 + Docker Desktop 共存时,三者嵌套限制会叠加,得逐层查 cat /proc/sys/fs/file-max、docker exec -it php-container bash -c 'ulimit -n'、再进容器里看 PHP 的实际限制——这种链路容易漏掉中间某一层。










