NGINX open-files limit silently ignored under SELinux

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

1,024

Open files per worker before, despite the config

65,000

Open files per worker after, verified in /proc

0

SELinux denials logged, which is why it hid

Environment

Configured limit versus the limit the workers actually ran withnginx.conf asked for 65,000 open files. SELinux denied the setrlimit call without logging, so the workers kept the systemd default of 1,024. A systemd LimitNOFILE drop-in grants 65,000 before NGINX starts.The config said one thing; the running workers did anotherBEFOREnginx.confworker_rlimit_nofile 65000setrlimit() deniedSELinux httpd_t, no AVC loggedWorkers: 1,024systemd default soft limitAFTERsystemd drop-inLimitNOFILE=65000:65000Limit set before startno setrlimit() neededWorkers: 65,000verified in /proc/<pid>/limitsnginx -t passed in both cases. Only the running process told the truth.

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.

The problem

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.

Investigation

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.

Root cause

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.

At a glance

Challenge

NGINX configured for 65,000 open files per worker, actually running with 1,024, with no SELinux denial logged.

Approach

Checked the running workers in /proc instead of trusting the config, ruled out ulimits, traced it to the SELinux httpd_t domain.

Result

All 4 workers verified at 65,000 open files; the alert stopped; both sites stayed up.

The fix

  1. Grant the limit from systemd: a drop-in at /etc/systemd/system/nginx.service.d/limits.conf with LimitNOFILE=65000:65000
  2. Restart, not reload: systemctl daemon-reload then a full restart, because only a fresh master process inherits new limits
  3. Verify the running workers: Max open files 65000 65000 in /proc/<pid>/limits for every worker, not just the config file
  4. Keep the rollback simple: delete the drop-in, reload systemd, restart NGINX

Outcome

The 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.

More case studies

WordPress Performance

1.8 s page load, down from 12 s

PHP2Shell Fleet Patching

500+ servers patched, zero downtime

Hypervisor I/O Stalls

40 VMs returned to stable I/O

Is your NGINX running the limits you think it is?

We check the live processes, not just the config, and fix what the logs do not tell you.