Fix Nginx 'Too Many Open Files' Error

Quick answer
Nginx's 'too many open files' error (errno 24) means a worker process hit its file descriptor limit. The fix requires changes at three levels — nginx.conf, the systemd unit, and the OS — all three are needed.
- Why This Happens
- Diagnose First
- The Fix — Three Layers Required
- Verify the Fix
- Complete Tuned /etc/nginx/nginx.conf
6 min read · Platform Engineering
Fix Nginx 'Too Many Open Files' Error
In /var/log/nginx/error.log:
2026/06/01 10:00:00 [crit] 12345#12345: *5000 open() "/var/www/html/index.html" failed (24: Too many open files), client: 203.0.113.1, server: example.com
Or when Nginx itself can't open its log:
nginx: [emerg] open() "/var/log/nginx/error.log" failed (24: Too many open files)
Error 24 = EMFILE — the Nginx worker process has hit its open file descriptor limit.
Why This Happens
Every open resource counts as a file descriptor in Linux:
- Each active client connection: 1 fd (client socket)
- Each upstream connection: 1 fd (upstream socket)
- Each static file being served: 1 fd
- Log files: 1 fd per log file per worker
Under traffic spikes, a single Nginx worker can need thousands of file descriptors simultaneously. The default OS limit is often 1024 — far too low for a production server.
Diagnose First
1# Current hard limit for the shell
2ulimit -Hn
3
4# Actual limit of the running Nginx master process
5cat /proc/$(cat /run/nginx.pid)/limits | grep "Max open files"
6# Max open files 1024 1024 files ← too low
7
8# Current fd usage by Nginx workers
9ls /proc/$(cat /run/nginx.pid)/fd | wc -l
10
11# Check how many worker processes are running
12ps aux | grep "nginx: worker"The Fix — Three Layers Required
The OS, systemd, and Nginx each enforce their own limits. Setting only one is not enough.
Layer 1: Nginx config — worker_rlimit_nofile
In /etc/nginx/nginx.conf, at the top level (before the events block):
1worker_processes auto; # Set to number of CPU cores
2worker_rlimit_nofile 65535; # Max open file descriptors per worker process
3
4events {
5 worker_connections 4096; # Max simultaneous connections per worker
6 use epoll; # Linux: use epoll I/O event model
7 multi_accept on; # Accept multiple connections per event loop tick
8}Rule of thumb: worker_rlimit_nofile ≥ worker_connections × 2 (each proxied connection needs at least a client socket and an upstream socket).
Layer 2: systemd service limit
Nginx on systemd ignores ulimit settings from the shell. It has its own LimitNOFILE in the service unit, which overrides the OS default.
Create an override:
systemctl edit nginxAdd:
[Service]
LimitNOFILE=65535Save, then apply:
systemctl daemon-reload
systemctl restart nginxVerify the systemd limit took effect:
cat /proc/$(cat /run/nginx.pid)/limits | grep "Max open files"
# Max open files 65535 65535 files ← correctLayer 3: OS limits
For non-systemd contexts (login shells, init scripts), set in /etc/security/limits.conf:
www-data soft nofile 65535
www-data hard nofile 65535
Replace www-data with the user Nginx runs as (check with ps aux | grep nginx | grep worker).
Also raise the system-wide kernel fd maximum:
echo "fs.file-max = 2097152" >> /etc/sysctl.d/99-nginx.conf
sysctl -p /etc/sysctl.d/99-nginx.confStuck on this in production?
We debug exactly this kind of issue for platform teams — usually in a single working session.
Verify the Fix
1# Confirm Nginx limit after restart
2cat /proc/$(cat /run/nginx.pid)/limits | grep "Max open files"
3
4# Monitor fd usage under load
5watch -n 2 'ls /proc/$(cat /run/nginx.pid)/fd | wc -l'
6
7# Confirm no more errno 24 in error log
8tail -f /var/log/nginx/error.log | grep "Too many open files"If fd usage under load stays well below 65535 and the error stops appearing, the fix worked.
Complete Tuned /etc/nginx/nginx.conf
1user www-data;
2worker_processes auto;
3worker_rlimit_nofile 65535; # ← Layer 1 fix
4pid /run/nginx.pid;
5
6events {
7 worker_connections 4096;
8 use epoll;
9 multi_accept on;
10}
11
12http {
13 sendfile on;
14 tcp_nopush on;
15 tcp_nodelay on;
16 keepalive_timeout 65;
17 types_hash_max_size 2048;
18 server_tokens off; # Don't expose Nginx version in headers
19
20 include /etc/nginx/mime.types;
21 default_type application/octet-stream;
22
23 access_log /var/log/nginx/access.log;
24 error_log /var/log/nginx/error.log warn;
25
26 gzip on;
27 gzip_types text/plain text/css application/json application/javascript;
28
29 include /etc/nginx/sites-enabled/*;
30}nginx -t && systemctl reload nginxQuick Reference
| Location | Setting | Value |
|---|---|---|
/etc/nginx/nginx.conf | worker_rlimit_nofile | 65535 |
systemctl edit nginx | LimitNOFILE | 65535 |
/etc/security/limits.conf | www-data hard nofile | 65535 |
/etc/sysctl.d/99-nginx.conf | fs.file-max | 2097152 |
All four must be set. Missing any one of them means the lowest limit wins.
See Also
- Nginx Load Balancing — upstream pools, algorithms, and keepalive
- Linux Commands for Advanced Engineers —
ulimit,sysctl, and systemd service tuning
Frequently Asked Questions
Why do I need to change limits in three places?
Because they nest. The kernel-wide maximum bounds everything, the per-process limit bounds the worker, and NGINX's own directive bounds connections per worker. Raising one and not the others means the lowest ceiling still applies, which is why a change that looks correct has no effect.
Why did my systemd service ignore the limits I set?
Because systemd does not read the shell limits file for services it starts — it applies its own. Set the limit in the unit or a drop-in override, then reload the daemon and restart the service. This is the most common reason the fix appears not to work.
How do I know what the limit should be?
worker_rlimit_nofile is per worker, so derive it from connections per worker with headroom — a proxied request consumes a descriptor on both the client and upstream side, so roughly twice worker_connections plus open files. The system-wide ceiling is what must account for every worker. Verify against the running process rather than trusting the configuration.
Could this be a leak rather than a limit?
Possibly. If descriptor count climbs steadily under stable traffic, something is not closing connections — often an upstream with keepalive misconfigured. Raising the ceiling buys time; watch whether the count plateaus after the change or simply takes longer to hit the new limit.
Official References
- nginx documentation — directives for proxying, load balancing and TLS
- Debug Pods — reading pod status, events and container states
- kubectl reference — command syntax, output formats and selectors
- Linux kernel admin guide — sysctl, cgroups and kernel tunables
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


