VPS Tutorials
Too many open files on a VPS: what to do (2026) — ulimit, nofile, LimitNOFILE troubleshooting
When Nginx, Node, a database or a self-hosted service on a VPS reports Too many open files, common causes include the process running out of file handles, systemd LimitNOFILE not taking effect, /etc/security/limits.conf only affecting login sessions, a mismatch in Nginx worker_connections, application connection leaks, or too many logs/sockets. This article troubleshoots layer by layer using ulimit -n, /proc/PID/limits, lsof, systemctl show and sysctl.

Seeing Too many open files on a VPS is usually not because there are too many files on disk, but because the process cannot open enough file handles at the same time.
Here "files" does not only mean real files; it also includes:
- TCP connections;
- Unix sockets;
- log files;
- database connections;
- pipes and temporary files;
- file descriptors opened internally by services such as Nginx, Node, Java, Redis and PostgreSQL.
So you may see symptoms like these:
EMFILE: too many open files
socket: Too many open files
accept4() failed (24: Too many open files)
open() failed: Too many open files
This article follows a hands-on VPS troubleshooting path: first confirm which process has exhausted its handles, then distinguish the shell's ulimit, systemd's LimitNOFILE, the global fs.file-max, Nginx worker limits and application connection leaks. Do not assume the problem is solved just because you changed one place.
In the current SSH session, check:
ulimit -n
ulimit -Sn
ulimit -Hn
Meaning:
-Sn: soft limit, the currently effective soft limit;-Hn: hard limit, the hard limit that a normal user cannot exceed;ulimit -n: usually shows the soft limit.
But note: the ulimit you see in SSH is not necessarily the same as the limit inside a systemd service. This is where many people misjudge the situation.
First look at the PID of the service reporting the error:
systemctl status nginx --no-pager
systemctl status app --no-pager
You can also use:
pidof nginx
pidof node
pidof mysqld
pidof postgres
To see how many files a process has already opened:
sudo ls /proc/PID/fd | wc -l
To see its limits:
cat /proc/PID/limits | grep -i 'open files'
If the output looks like:
Max open files 1024 4096 files
and /proc/PID/fd is already close to 1024, it is very likely that this process has hit its limit.
Count the number of files opened by a process:
sudo lsof -p PID | wc -l
Look at the first part:
sudo lsof -p PID | head -n 30
If you want to look at a certain type of connection, you can combine it with ss:
sudo ss -antp | grep PID
Common interpretations:
- A large number of
TCP ESTAB: many connections, possibly high concurrency or a connection leak; - A large number of
CLOSE-WAIT: the application is not closing connections properly; - A large number of log files: log rotation issues or the program opening files without releasing them;
- A large number of sockets: the reverse proxy, WebSocket or database connection pool may be too large;
- A large number of deleted files: files were deleted but the process still holds the handles.
Find deleted files:
sudo lsof | grep deleted | head
If there are many deleted files, restarting the corresponding process is the only way to release the handles and the space.
If the service is managed by systemd, changing only /etc/security/limits.conf is usually not enough.
First check the service's current limits:
systemctl show nginx -p LimitNOFILE
systemctl show app -p LimitNOFILE
Using an override is recommended:
sudo systemctl edit app
Write:
[Service]
LimitNOFILE=65535
Then:
sudo systemctl daemon-reload
sudo systemctl restart app
systemctl show app -p LimitNOFILE
Then confirm whether it took effect inside the process:
cat /proc/$(pidof app)/limits | grep -i 'open files'
If there are multiple process names or a master/worker structure, such as Nginx, confirm that the worker processes also received the new limit.
Many tutorials tell you to change:
sudo nano /etc/security/limits.conf
For example:
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
This is useful for shell sessions that log in through PAM, such as commands run after an SSH login. But systemd services do not necessarily follow this configuration.
If after changing it you find:
ulimit -n
still shows the old value, check:
grep pam_limits /etc/pam.d/common-session
However, for long-running services on a VPS, prefer changing LimitNOFILE rather than relying only on limits.conf.
Check the system-wide open file status:
cat /proc/sys/fs/file-nr
sysctl fs.file-max
file-nr generally has three numbers; the first column is the number of allocated file handles, and the third column is the system maximum.
If the global value is also close to its limit, you can adjust it temporarily:
sudo sysctl -w fs.file-max=2097152
Permanent configuration:
echo 'fs.file-max = 2097152' | sudo tee /etc/sysctl.d/99-file-max.conf
sudo sysctl --system
But on most VPSs, the per-process nofile limit of Too many open files is hit first, not the global fs.file-max. Check /proc/PID/limits first; do not change the global value right away.
Common Nginx error:
accept4() failed (24: Too many open files)
worker_connections are not enough
Besides systemd's LimitNOFILE, also check Nginx's own configuration:
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
}
Check the configuration:
sudo nginx -t
sudo systemctl reload nginx
Roughly speaking: one Nginx connection may occupy more than one file descriptor, and a reverse proxy also connects to upstreams, so do not set worker_connections to exactly the file handle limit.
If Nginx also has WebSocket, long-lived connections or reverse proxying to applications in front of it, the connection count is even more likely to multiply.
If a Node, Java, Python or Go service reports EMFILE, do not just increase nofile. First confirm whether the application is failing to close files or connections.
Check connection states:
sudo ss -antp | grep PID | awk '{print $1}' | sort | uniq -c
If there are many CLOSE-WAIT, it usually means the application is not closing sockets properly.
Also check the database connection pool:
- whether the pool's maximum connection count is too large;
- whether connections are returned after a request fails;
- whether a new connection is created on every request;
- whether slow queries keep connections occupied for a long time;
- whether the connection counts of the Nginx / application / database layers match.
If you only change LimitNOFILE from 1024 to 65535 but the leak remains, it will still be exhausted again after a while.
The limits seen inside a container may differ from those on the host.
Enter the container:
docker exec -it 容器名 sh
ulimit -n
Docker Compose can set:
services:
app:
ulimits:
nofile:
soft: 65535
hard: 65535
If the container is managed by systemd, also check the Docker service limits on the host at the same time:
systemctl show docker -p LimitNOFILE
For Docker permission and mount issues, see: Docker Permission Denied on a VPS: what to do.
When errors are already happening in production, temporary recovery is usually:
sudo systemctl restart app
This releases the file descriptors opened by the process, but it only stops the bleeding.
A long-term fix requires three things:
- Raise the nofile limit in the correct place;
- Find out why the file descriptor count is growing;
- Add file descriptor usage alerts to monitoring.
You can check periodically:
ls /proc/PID/fd | wc -l
cat /proc/PID/limits | grep -i 'open files'
If the file descriptor count rises monotonically over time and never falls back, it is basically a leak or connections not being released.
When you encounter Too many open files, check in this order:
systemctl status 服务名 --no-pagerto find the service reporting the error.journalctl -u 服务名 -n 100 --no-pagerto see the specific error.pidof 服务名to find the PID.ls /proc/PID/fd | wc -lto see the number of open file descriptors.cat /proc/PID/limits | grep -i 'open files'to see the process limits.lsof -p PIDto see whether the open items are files, sockets or deleted files.- For systemd services, use
systemctl edit 服务名to setLimitNOFILE=65535. - For Nginx, check both
worker_rlimit_nofileandworker_connections. - For Docker containers, check the compose
ulimits. - If the file descriptor count keeps growing, investigate connection leaks, unreleased log files and the database connection pool.
The core of Too many open files is not "too many files", but a mismatch between the process file handle limit and the actual connection/file usage.
First check /proc/PID/limits to confirm the Max open files the process received; then check /proc/PID/fd and lsof to confirm who is actually occupying the file descriptors. For systemd services, prefer changing LimitNOFILE; for Nginx, also configure worker_rlimit_nofile; for Docker containers, set ulimits separately. If the file descriptor count keeps rising, raising the limit is not enough; you must keep investigating connection leaks or file handle leaks.