VPS 上出现 Too many open files,通常不是磁盘文件太多,而是进程能同时打开的文件句柄不够。
这里的“文件”不只是真正的文件,还包括:
- TCP 连接;
- Unix socket;
- 日志文件;
- 数据库连接;
- 管道和临时文件;
- Nginx、Node、Java、Redis、PostgreSQL 这类服务内部打开的 fd。
所以你可能看到这些现象:
EMFILE: too many open files
socket: Too many open files
accept4() failed (24: Too many open files)
open() failed: Too many open files
这篇按 VPS 实战排查:先确认是哪个进程耗尽句柄,再区分 shell 的 ulimit、systemd 的 LimitNOFILE、全局 fs.file-max、Nginx worker 限制和应用连接泄漏。不要只改一个地方就以为解决了。
当前 SSH 会话里看:
ulimit -n
ulimit -Sn
ulimit -Hn
含义:
-Sn:soft limit,当前生效的软限制;-Hn:hard limit,普通用户不能超过的硬限制;ulimit -n:通常显示 soft limit。
但注意:你在 SSH 里看到的 ulimit,不一定等于 systemd 服务里的限制。很多人就在这里误判。
先看报错服务的 PID:
systemctl status nginx --no-pager
systemctl status app --no-pager
也可以用:
pidof nginx
pidof node
pidof mysqld
pidof postgres
看某个进程已经打开多少文件:
sudo ls /proc/PID/fd | wc -l
看它的限制:
cat /proc/PID/limits | grep -i 'open files'
如果输出类似:
Max open files 1024 4096 files
而 /proc/PID/fd 已经接近 1024,就很可能是这个进程撞到限制了。
统计进程打开文件数量:
sudo lsof -p PID | wc -l
看前面一部分:
sudo lsof -p PID | head -n 30
如果想看某类连接,可以结合 ss:
sudo ss -antp | grep PID
常见判断:
- 大量
TCP ESTAB:连接很多,可能是高并发或连接泄漏; - 大量
CLOSE-WAIT:应用没有正确关闭连接; - 大量日志文件:日志轮转或程序打开文件没释放;
- 大量 socket:反向代理、WebSocket、数据库连接池可能过大;
- 大量 deleted 文件:文件删了但进程还占着句柄。
找 deleted 文件:
sudo lsof | grep deleted | head
如果 deleted 文件很多,重启对应进程才能释放句柄和空间。
如果服务是 systemd 管理的,只改 /etc/security/limits.conf 通常不够。
先看服务当前限制:
systemctl show nginx -p LimitNOFILE
systemctl show app -p LimitNOFILE
推荐用 override:
sudo systemctl edit app
写入:
[Service]
LimitNOFILE=65535
然后:
sudo systemctl daemon-reload
sudo systemctl restart app
systemctl show app -p LimitNOFILE
再确认进程里是否生效:
cat /proc/$(pidof app)/limits | grep -i 'open files'
如果是多个进程名或 master/worker 结构,比如 Nginx,要确认 worker 进程也拿到了新限制。
很多教程会让你改:
sudo nano /etc/security/limits.conf
例如:
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
这对通过 PAM 登录的 shell 会话有用,比如 SSH 登录后运行的命令。但 systemd 服务不一定吃这套配置。
如果你改完后发现:
ulimit -n
还是旧值,检查:
grep pam_limits /etc/pam.d/common-session
不过对 VPS 上的长期服务,优先改 LimitNOFILE,不要只依赖 limits.conf。
看系统全局打开文件状态:
cat /proc/sys/fs/file-nr
sysctl fs.file-max
file-nr 一般有三个数字,第一列是已分配文件句柄数量,第三列是系统最大值。
如果全局也接近上限,可以临时调整:
sudo sysctl -w fs.file-max=2097152
永久配置:
echo 'fs.file-max = 2097152' | sudo tee /etc/sysctl.d/99-file-max.conf
sudo sysctl --system
但多数 VPS 的 Too many open files 是单个进程的 nofile 限制先撞了,不是全局 fs.file-max。先看 /proc/PID/limits,不要一上来就改全局。
Nginx 常见错误:
accept4() failed (24: Too many open files)
worker_connections are not enough
除了 systemd 的 LimitNOFILE,还要看 Nginx 自己的配置:
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
}
检查配置:
sudo nginx -t
sudo systemctl reload nginx
粗略理解:Nginx 一个连接可能占用不止一个 fd,反向代理还会连接上游,所以不要把 worker_connections 设置得刚好等于文件句柄上限。
如果 Nginx 前面还有 WebSocket、长连接、反向代理到应用,连接数会更容易放大。
如果是 Node、Java、Python、Go 服务报 EMFILE,不要只加大 nofile。先确认是不是应用没有关闭文件或连接。
看连接状态:
sudo ss -antp | grep PID | awk '{print $1}' | sort | uniq -c
如果 CLOSE-WAIT 很多,通常是应用没有正确关闭 socket。
数据库连接池也要看:
- pool 最大连接数是否太大;
- 请求失败后连接有没有归还;
- 是否每次请求都新建连接;
- 是否有慢查询导致连接长时间占用;
- Nginx / 应用 / 数据库三层连接数是否匹配。
如果只是把 LimitNOFILE 从 1024 改到 65535,但泄漏还在,过一段时间还是会再次耗尽。
容器内看到的限制可能和宿主机不同。
进入容器:
docker exec -it 容器名 sh
ulimit -n
Docker Compose 可以设置:
services:
app:
ulimits:
nofile:
soft: 65535
hard: 65535
如果容器由 systemd 管理,还要同时检查宿主机上的 Docker 服务限制:
systemctl show docker -p LimitNOFILE
Docker 相关权限和挂载问题可以看:VPS 上 Docker Permission Denied 怎么办。
线上已经报错时,临时恢复通常是:
sudo systemctl restart app
这会释放进程打开的 fd,但只是止血。
长期修复要做三件事:
- 提高正确位置的 nofile 限制;
- 找出 fd 为什么增长;
- 给监控加上 fd 使用量告警。
可以定期看:
ls /proc/PID/fd | wc -l
cat /proc/PID/limits | grep -i 'open files'
如果 fd 数量随时间单调上升,从不回落,基本就是泄漏或连接没有释放。
遇到 Too many open files,按这个顺序查:
systemctl status 服务名 --no-pager找到报错服务。journalctl -u 服务名 -n 100 --no-pager看具体错误。pidof 服务名找 PID。ls /proc/PID/fd | wc -l看已打开 fd 数量。cat /proc/PID/limits | grep -i 'open files'看进程限制。lsof -p PID看打开的是文件、socket 还是 deleted 文件。- systemd 服务用
systemctl edit 服务名设置LimitNOFILE=65535。 - Nginx 同时检查
worker_rlimit_nofile和worker_connections。 - Docker 容器检查 compose 的
ulimits。 - 如果 fd 持续增长,排查连接泄漏、日志文件未释放和数据库连接池。
Too many open files 的核心不是“文件太多”,而是进程文件句柄限制和实际连接/文件使用量不匹配。
先看 /proc/PID/limits,确认进程拿到的 Max open files;再看 /proc/PID/fd 和 lsof,确认到底是谁占用了 fd。systemd 服务优先改 LimitNOFILE,Nginx 还要配 worker_rlimit_nofile,Docker 容器要单独设 ulimits。如果 fd 数量持续上涨,只加限制不够,必须继续查连接泄漏或文件句柄泄漏。
