NGINX was configured for 65,000 open files per worker and reported a clean start. Every worker was actually running with 1,024. SELinux blocked the change without logging a denial, so nothing looked wrong until we checked the running processes.
September 2026 · 3 min read
SECTOR
Our own production web server
SCALE
Two live WordPress sites, 4 NGINX workers
PLATFORM
RHEL-family Linux, SELinux enforcing, NGINX, systemd
ENGAGEMENT
Proactive capacity audit
Open files per worker before, despite the config
Open files per worker after, verified in /proc
SELinux denials logged, which is why it hid
Scroll the diagram sideways to see all of it →
Same configuration file, same clean config test. The difference is where the limit is granted: inside NGINX (denied by SELinux) or by systemd before NGINX starts.
A limit that looked set but was not. During a routine capacity audit of our own web server, the NGINX error log showed the same line on every start: [alert] setrlimit(RLIMIT_NOFILE, 65000) failed (13: Permission denied). The configuration test passed and both sites served normally, so nothing else hinted at it. In reality each worker could hold only 1,024 open files, a small fraction of the 8,192 connections it was configured for.
Not a ulimit problem. The same limit change succeeded for the nginx user in a normal shell, so file permissions and user limits were ruled out. That pointed to SELinux: the httpd_t domain NGINX runs in is not allowed to raise its own resource limits. The denial is marked “dontaudit”, so ausearch -m AVC showed nothing at all.
A config test that passes and a service that starts cleanly can still hide a setting that never took effect.
The limit was requested in the wrong place. worker_rlimit_nofile asks NGINX to raise its own limit after it starts. Under SELinux enforcing, that call is refused, and the workers keep the systemd default soft limit of 1,024. The fix is to grant the limit before NGINX starts, from systemd, so NGINX never has to ask.
NGINX configured for 65,000 open files per worker, actually running with 1,024, with no SELinux denial logged.
Checked the running workers in /proc instead of trusting the config, ruled out ulimits, traced it to the SELinux httpd_t domain.
All 4 workers verified at 65,000 open files; the alert stopped; both sites stayed up.
/etc/systemd/system/nginx.service.d/limits.conf with LimitNOFILE=65000:65000systemctl daemon-reload then a full restart, because only a fresh master process inherits new limitsMax open files 65000 65000 in /proc/<pid>/limits for every worker, not just the config fileThe limit that was always meant to be there. All four workers now run with 65,000 open files, the alert no longer appears on start, and both sites returned 200 throughout. We now check this on every NGINX server we take over that runs SELinux enforcing, by reading the live process limits rather than the configuration.
We check the live processes, not just the config, and fix what the logs do not tell you.