Fixing "Too many open files" - Resolving File Descriptor Exhaustion

Fixing "Too many open files" - Resolving File Descriptor Exhaustion

What You Will Solve Here

  • Understand the two FD limits (per-process EMFILE vs system-wide ENFILE) and which one you hit
  • Use ulimit and lsof to check current FD usage immediately
  • Permanently raise limits via limits.conf, sysctl.conf, or a systemd unit override

Fastest path (3 steps)

  1. ulimit -n or /proc/<pid>/limits to identify which ceiling you hit
  2. lsof -p <pid> | wc -l to see how many FDs the process actually has open
  3. Fix permanently with /etc/security/limits.conf (PAM-based processes) or LimitNOFILE= (systemd services)

Assumed environment

  • OS: Ubuntu / Debian / RHEL family
  • Privilege: sudo available
  • systemd-based service management assumed

What Is "Too many open files"?

A file descriptor (FD) is an integer handle the kernel uses to track open files, sockets, pipes, and other I/O objects. Every time a process opens a file or accepts a connection, it consumes one FD.

Linux enforces two independent limits:

  • Per-process limit (EMFILE): how many FDs a single process can hold at once. Checked with ulimit -n.
  • System-wide limit (ENFILE): total FDs open across all processes. Checked via /proc/sys/fs/file-max.

In practice, the error almost always comes from the per-process limit (default: 1024), which is far too low for production web servers, databases, or Node.js apps handling many concurrent connections.

Classic symptoms

  • Nginx / Apache / MySQL / Node.js starts throwing errors under load
  • Logs contain Too many open files (EMFILE) or accept4: Too many open files
  • Connection count stalls near the value of ulimit -n (often 1024)

1. Check Current FD Usage

1-1. Check the limit for the current shell

$ ulimit -n     # soft limit (effective value)
$ ulimit -Hn    # hard limit (ceiling a process can raise itself to)
1024
1048576

The soft limit (1024) is the working value. A process can raise it up to the hard limit without special privileges.

1-2. Check a specific process

$ cat /proc/<pid>/limits | grep 'open files'
Limit                     Soft Limit           Hard Limit           Units
Max open files            65536                65536                files

Count how many FDs the process currently holds:

$ ls /proc/<pid>/fd | wc -l
# or
$ sudo lsof -p <pid> | wc -l

1-3. Check system-wide FD usage

$ cat /proc/sys/fs/file-nr
13472	0	524288

Three columns: currently allocated / free-but-reserved (always 0) / system ceiling.

Check the ceiling directly:

$ sysctl fs.file-max
fs.file-max = 524288

The per-process limit (ulimit -n) runs out long before the system ceiling in nearly every real-world case. Only raise fs.file-max if /proc/sys/fs/file-nr shows the first column approaching the third.

2. Identify the Offending Process

2-1. Rank processes by FD count

$ sudo lsof 2>/dev/null | awk '{print $1, $2}' | sort | uniq -c | sort -rn | head -20
   4821 nginx   1234
   3102 mysqld  5678
   1204 node    9012

2-2. Inspect what a process has open

$ sudo lsof -p <pid>
COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   1234  www    0r   REG   8,1   1234      /var/log/nginx/access.log
nginx   1234  www    3u  IPv4  56789       0t0  TCP *:80 (LISTEN)

A large number of sockets often points to a connection leak — connections accepted but never closed.

2-3. Check socket state

$ ss -s
Total: 12034
TCP:   11820 (estab 8000, closed 200, orphaned 20, timewait 200)

An unusually large estab count suggests the application is not closing connections correctly.

3. Temporary Fix (Immediate Relief)

This changes the limit only for the current shell session. It resets on logout.

$ ulimit -n 65536

To change the limit of an already-running process without restarting it:

$ sudo prlimit --pid <pid> --nofile=65536:65536

ulimit changes are session-scoped. See the next section for permanent configuration.

4. Permanent Configuration

4-1. PAM-based processes (login sessions and conventional daemons)

Add to /etc/security/limits.conf or a file in /etc/security/limits.d/:

$ sudo vi /etc/security/limits.d/99-fd-limits.conf
# * applies to all users; replace with a username for a specific user
*    soft    nofile    65536
*    hard    nofile    65536

# Specific user example
www-data soft nofile 65536
www-data hard nofile 65536

The change takes effect on next login or service restart. Verify:

$ su - www-data -s /bin/bash -c 'ulimit -n'
65536

Ubuntu 18.04+ also lets you set the system-wide default with DefaultLimitNOFILE= in /etc/systemd/system.conf. Because this affects every service, prefer per-service overrides (4-3) unless you need a blanket change.

4-2. Raise the system-wide ceiling (fs.file-max)

Usually unnecessary — fix the per-process limit first. Only do this if file-nr shows the allocated count approaching the ceiling.

$ sudo sysctl -w fs.file-max=1048576    # immediate, resets on reboot

To persist across reboots, create /etc/sysctl.d/99-fd.conf:

$ sudo vi /etc/sysctl.d/99-fd.conf
fs.file-max = 1048576
$ sudo sysctl --system    # reload all sysctl.d/*.conf files

4-3. systemd-managed services

systemd does not go through PAM, so limits.conf has no effect. Override per service:

$ sudo systemctl edit nginx    # opens an editor for the override

Add and save:

[Service]
LimitNOFILE=65536

Or create the file manually:

$ sudo mkdir -p /etc/systemd/system/nginx.service.d/
$ sudo vi /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

Verify the limit took effect:

$ cat /proc/$(pgrep -o nginx)/limits | grep 'open files'
Max open files            65536                65536                files

LimitNOFILE=infinity is available on kernel 5.15+. On older kernels, specify an explicit number (65536 or 1048576).

5. What Not to Do

Next Reading