ulimit -n 65536仍报错因软限受硬限压制、docker/systemd/ide共用句柄且不继承shell限制;须同步调高硬限、透传ulimits至容器、为systemd服务单独配置limitnofile,并验证proc/1/fd占用量。

直接改 ulimit -n 很大概率没用,或者只管一时——因为 Composer 启动时继承的是 shell 的软限制,而软限制又受硬限制压制;更关键的是,Docker 容器、systemd 服务、IDE 后台进程都在偷偷吃句柄,你调了 Composer 却漏了它们。
为什么 ulimit -n 65536 还是报错?
常见现象是执行完 ulimit -n 65536,再跑 composer install 依然卡在 Too many open files。原因有三:
-
ulimit -n只改当前 shell 的软限制,但硬限制(ulimit -Hn)可能仍是 4096,导致软限被静默截断 - Composer 在解压
.tar.gz、扫描vendor/、并行下载时,会瞬间打开上千个文件描述符——不是“慢慢开”,是“爆发式开” - 如果你在 Docker 或 CI 环境里跑,宿主机的
ulimit对容器内进程完全无效,必须显式透传
临时验证必须分两步走
别一上来就改配置,先确认是不是真被卡住:
- 运行
ulimit -Sn和ulimit -Hn,两个值都得 ≥ 65536 才算真正生效 - 进容器或目标环境后,执行
ls /proc/1/fd | wc -l,如果接近或等于ulimit -n输出值,就是它了 - 顺手查下全局池:
cat /proc/sys/fs/file-max,若低于 200000,单调进程上限也白搭
永久生效要绕过三个典型陷阱
改完 /etc/security/limits.conf 却不生效?多半栽在这三处:
- 没确认 PAM 是否加载:检查
/etc/pam.d/common-session(Debian/Ubuntu)或/etc/pam.d/system-auth(RHEL/CentOS)是否含未注释的session required pam_limits.so - 改完没重新登录:
source ~/.bashrc或新开 terminal 标签页都不行,必须 SSH 重连或 GUI 登出重进 - systemd 服务完全无视
limits.conf:Nginx、PHP-FPM、自建部署脚本全得单独加LimitNOFILE=65536到 service 文件里
Docker Compose 场景下 ulimits 配置容易写错
很多人把 ulimits 嵌套在 resources 下,或者只设 soft 不设 hard,结果配置根本不加载:
- 正确写法必须与
image、ports同级,且hard≥soft:services: app: image: php:8.3 ulimits: nofile: soft: 65536 hard: 65536 - 宿主机 dockerd 进程本身也受 systemd 限制,需同步改
/etc/docker/daemon.json加default-ulimits,否则容器继承的是 dockerd 的低值 - macOS 用户注意:
ulimit命令在 Terminal 里有效,但 launchd 会覆盖它——得配~/Library/LaunchAgents/limit.maxfiles.plist才能真正持久
最常被忽略的一点:错误日志从不告诉你哪个进程占满了句柄。遇到反复失败,先用 lsof -u $USER | wc -l 看总量,再 lsof -u $USER -p $(pgrep -f 'composer') 2>/dev/null | wc -l 锁定 Composer 自身开的数量——比盲目调高数字靠谱得多。











