Fix Nginx 'Too Many Open Files' Error
The Error: Too Many Open Files
Nginx starts dropping connections under load. The error log shows:
2026/03/30 09:15:32 [alert] 2847#0: *198234 socket() failed
(24: Too many open files) while connecting to upstream
Or during startup:
nginx: [warn] 2048 worker_connections exceed open file resource limit: 1024
Clients experience refused connections, timeouts, or intermittent 500 errors during traffic spikes.
Root Cause
Every active connection in Nginx requires at least two file descriptors (one for the client socket, one for the upstream or static file). On Linux, processes have a default limit of 1024 open file descriptors. A busy Nginx server with worker_connections 4096 needs at least 8192 file descriptors per worker, but the OS is capping it at 1024.
The limit comes from three layers, and all three must be raised:
- Kernel limit (
fs.file-max) — system-wide maximum - User limit (
ulimit -n) — per-user/per-process maximum - Nginx config (
worker_rlimit_nofile) — Nginx's own cap
Step-by-Step Fix
1. Check current limits
# System-wide kernel limit
cat /proc/sys/fs/file-max
# Current open files across the system
cat /proc/sys/fs/file-nr
# Output: 5120 0 2097152 (allocated / free / max)
# Nginx worker process limit
cat /proc/$(pgrep -f 'nginx: worker' | head -1)/limits | grep "open files"
# Max open files 1024 1024 files
# Nginx configuration
nginx -T 2>/dev/null | grep -E 'worker_rlimit_nofile|worker_connections'
2. Increase the kernel limit
# Temporary (until reboot)
sudo sysctl -w fs.file-max=2097152
# Permanent
echo "fs.file-max = 2097152" | sudo tee -a /etc/sysctl.d/99-file-limits.conf
sudo sysctl -p /etc/sysctl.d/99-file-limits.conf
3. Increase per-user limits
Edit /etc/security/limits.conf:
# /etc/security/limits.conf
www-data soft nofile 65535
www-data hard nofile 65535
root soft nofile 65535
root hard nofile 65535
For systemd-managed Nginx, override the service unit:
sudo systemctl edit nginx
Add:
[Service]
LimitNOFILE=65535
This creates /etc/systemd/system/nginx.service.d/override.conf.
sudo systemctl daemon-reload
sudo systemctl restart nginx
4. Configure Nginx's own limit
Edit /etc/nginx/nginx.conf:
# Set at the top level, outside any block
worker_rlimit_nofile 65535;
events {
worker_connections 16384; # Must be <= worker_rlimit_nofile
multi_accept on;
use epoll;
}
A good rule of thumb: worker_rlimit_nofile should be at least 2 * worker_connections.
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
5. Verify the fix
# Check the new limits on the worker process
cat /proc/$(pgrep -f 'nginx: worker' | head -1)/limits | grep "open files"
# Max open files 65535 65535 files
# Monitor file descriptor usage under load
watch -n 1 "ls /proc/$(pgrep -f 'nginx: worker' | head -1)/fd | wc -l"
6. Quick reference: the three layers
| Layer | Setting | File |
|---|---|---|
| Kernel | fs.file-max | /etc/sysctl.d/99-file-limits.conf |
| User/Process | LimitNOFILE | systemctl edit nginx |
| Nginx | worker_rlimit_nofile | /etc/nginx/nginx.conf |
All three must be aligned. The effective limit is the minimum of the three.
Prevention Tips
- Set these limits in your provisioning automation (Ansible, Packer, cloud-init) so every new server is configured from day one.
- Calculate capacity:
workers * worker_connections * 2gives the minimum file descriptors needed. Sizeworker_rlimit_nofileabove that. - Monitor with Prometheus: export
process_open_fdsfrom the Nginx exporter and alert at 80% of the limit. - Use
worker_connectionswisely — each idle keepalive connection still holds a file descriptor. Setkeepalive_timeoutto a reasonable value (15-30s). - Load test before production to validate limits under realistic traffic patterns.
Was this article helpful?
CI/CD Engineering Lead
Automation evangelist who believes no deployment should require a human. I write pipelines, break pipelines, and write about both. Code-first, always.
Related Articles
Fix Nginx 502 Bad Gateway Behind a Reverse Proxy
Diagnose and fix Nginx 502 Bad Gateway errors when proxying to upstream backends — check sockets, timeouts, and upstream health.
Nginx Reverse Proxy & Caching: The Complete Guide
Set up Nginx as a reverse proxy with SSL termination, response caching, rate limiting, and load balancing for high-traffic applications.
Fix Linux 'Too Many Open Files' System-Wide
Resolve the Linux 'too many open files' error by increasing file descriptor limits at the system, user, and process level.
Nginx Load Balancing: Round Robin, Least Conn, and IP Hash
Configure Nginx upstream load balancing with round robin, least connections, and IP hash strategies including health checks and failover.
Nginx Rate Limiting in Production: Protect Your APIs
Implement Nginx rate limiting using limit_req and limit_conn to protect APIs from abuse, brute force, and traffic spikes in production.
Fix Grafana Dashboards That Load Forever
Fix Grafana dashboards that hang or take minutes to load by optimizing queries, reducing panel count, and tuning datasource settings.
More in Nginx
View all →Apache mod_proxy: Reverse Proxy, Load Balancing, and WebSocket Support
How to use Apache httpd as a reverse proxy with mod_proxy — proxying to backend services, load balancing across multiple upstreams, WebSocket proxying, and health check configuration.
Apache httpd: Virtual Hosts, SSL/TLS, and URL Rewriting in Production
How to configure Apache httpd for production use — name-based virtual hosts, SSL/TLS with Let's Encrypt, HTTP to HTTPS redirects, mod_rewrite rules, and performance tuning with MPM.
Envoy Proxy: Architecture, xDS Configuration, and Getting Started
An introduction to Envoy Proxy's architecture — listeners, clusters, filters, and the xDS dynamic configuration API. Covers static configuration for standalone use and how Envoy fits into service meshes like Istio.
Envoy Traffic Management: Retries, Timeouts, Canary Deployments, and Rate Limiting
Advanced Envoy traffic management — configuring retries with exponential backoff, per-request timeouts, weighted canary routing, global rate limiting, and fault injection for resilience testing.