Skip to main content
Fix "Too Many Open Files" on Linux (ulimit)

Fix "Too Many Open Files" on Linux (ulimit)

KU
• 7 min read•Updated •67 views

When a process hits its file descriptor ceiling everything breaks at once; here is how to find the real limit and raise it correctly.

Key takeaways

  • Too many open files (EMFILE) means a process hit its per-process file descriptor limit, often the 1024 soft default.
  • Read the live limit in /proc/PID/limits, count fds with ls /proc/PID/fd | wc -l, then raise it where the process starts: LimitNOFILE= for systemd services, limits.conf for PAM logins, ulimit -n for shells.

"Too many open files" (EMFILE) means one process hit its file-descriptor limit, usually the 1024 soft default. Check the live value with grep "open files" /proc/PID/limits, count usage with ls /proc/PID/fd | wc -l, then raise it where the process starts: LimitNOFILE= for systemd services, limits.conf for login sessions, ulimit -n for a shell.

The symptom: logs fill with accept: too many open files, new connections hang, and the health check flaps. Every socket, pipe, epoll instance, and open file counts against the limit, and the limit is per-process, so knowing how to track per-process resource usage (see process management and monitoring in depth) decides where you fix it.

What the error actually means

A file descriptor (fd) is a small integer the kernel hands back for anything openable. The soft limit RLIMIT_NOFILE caps how many a single process may hold at once. Cross it and open(), socket(), or accept() fail with EMFILE. You will see it as Too many open files in strace, application logs, or a Java java.net.SocketException.

Two limits exist per process. The soft limit is the enforced value. The hard limit is the ceiling an unprivileged process may raise its own soft limit to; only a process with CAP_SYS_RESOURCE can raise the hard limit.

See the current limits

bash.bash
$ ulimit -Sn        # soft
1024
$ ulimit -Hn        # hard
524288

ulimit reports the limits of your current shell. It does not tell you what a running daemon inherited. The daemon may have been started by systemd, cron, or an init script with a completely different environment. Read its actual limits from procfs:

bash.bash
$ cat /proc/$(pgrep -o nginx)/limits | grep -i "open files"
Max open files            1024                 4096                 files

That first column is the soft limit the process is really running with. If it says 1024 while your shell says 524288, your shell config is irrelevant to that process.

Count how many fds a process is holding

bash.bash
$ ls /proc/12345/fd | wc -l
1017

At 1017 against a soft limit of 1024 you are about to fall over. To see what those fds are, lsof is the readable view:

bash.bash
$ lsof -p 12345 | awk '{print $5}' | sort | uniq -c | sort -rn
    880 IPv4
     71 REG
     12 unix
      8 DIR
      5 CHR

880 open IPv4 sockets on a process that should hold a few dozen is a leak signature, not a limit that is too low. Hold that thought.

Raise the limit the right way, per context

There is no single place to change this. Where you edit depends on how the process starts.

Interactive shell or a script you launch by hand: ulimit -n 65536 in that shell, before starting the program. It applies to that shell and its children only, and it cannot exceed the hard limit.

Login sessions (SSH, su, getty): edit /etc/security/limits.conf and rely on pam_limits. This path only affects processes started through PAM.

code
# /etc/security/limits.conf
appuser   soft   nofile   65536
appuser   hard   nofile   131072

Confirm pam_limits.so is present in the relevant PAM stack (/etc/pam.d/common-session or /etc/pam.d/sshd), then log out and back in. A common trap: editing limits.conf and expecting a systemd-managed service to pick it up. It will not. PAM is not in that path.

systemd services: set it in the unit, not in limits.conf. systemd ignores pam_limits for services it starts.

ini.ini
# /etc/systemd/system/myapp.service  (or a drop-in)
[Service]
LimitNOFILE=65536

Use a drop-in to avoid editing packaged units:

bash.bash
$ sudo systemctl edit myapp
# add the [Service] block above, then:
$ sudo systemctl daemon-reload
$ sudo systemctl restart myapp

daemon-reload alone does not change a running process. The service must restart to inherit the new limit.

The system-wide ceiling

Per-process limits sit under two kernel-wide sysctls. fs.file-max is the total number of open file handles the whole system will allow. fs.nr_open is the maximum any single process limit may be set to, so a LimitNOFILE higher than fs.nr_open is silently clamped.

bash.bash
$ sysctl fs.file-max fs.nr_open
fs.file-max = 9223372036854775807
fs.nr_open = 1048576

When file-max is exhausted the error is ENFILE ("Too many open files in system"), not EMFILE. On many current distros it reads as the 64-bit maximum shown above, so you rarely touch it. If you genuinely need per-process limits above ~1M, raise fs.nr_open first in /etc/sysctl.d/, then set the higher LimitNOFILE.

Verify it actually applied

Do not trust the config file. Trust procfs on the live PID after restart:

bash.bash
$ systemctl show myapp -p MainPID --value
12345
$ grep "open files" /proc/12345/limits
Max open files            65536                65536                files

If that still reads 1024, you edited the wrong place for how the process starts. That mismatch is the single most common reason this error "won't go away."

Root cause vs bandaid

Raising the limit is correct when the workload legitimately needs more fds: a busy reverse proxy, a database with many connections. It is a bandaid when the fd count climbs without bound and never plateaus. Common leak sources:

  • Connection pools: a pool with no max, or code that opens a client per request and never closes it. The fix is in the pool config, not the kernel.
  • Unclosed files or sockets: a missing close() in an error path. Watch /proc/<pid>/fd over time; steady growth under steady load means a leak.
  • inotify watches: watchers count against fs.inotify.max_user_watches, a separate limit. A tool watching a huge tree throws ENOSPC ("no space left on device") rather than EMFILE, but it is the same class of exhaustion. Raise it with sysctl fs.inotify.max_user_watches=524288 if the watch count is real.

If you raise ulimit and the process refills the new headroom in an hour, you bought an hour. Sample the fd count over time before deciding.

This is one entry in the broader Linux troubleshooting guide; the same procfs-first instinct applies when you hit address already in use.

Questions people ask

What does "accept: too many open files" mean?

A listening server called accept() for a new connection and the kernel refused, because the process already holds as many descriptors as its RLIMIT_NOFILE soft limit allows. Each accepted connection is a new fd, so busy servers hit this first. Raise LimitNOFILE= if the load is real; if the fd count keeps growing under steady traffic, connections are not being closed.

Why doesn't editing limits.conf fix my systemd service?

/etc/security/limits.conf is applied by pam_limits, and only processes started through a PAM session (SSH, su, console login) pass through PAM. systemd services do not. As of October 2026 they get systemd's default of 1024 soft and 524288 hard (DefaultLimitNOFILE=1024:524288) unless the unit sets LimitNOFILE=. Add it in a drop-in, daemon-reload, and restart the service.

What is a safe value for LimitNOFILE?

For a busy proxy or database, 65536 is a common, safe starting point; it stays far under the kernel ceiling fs.nr_open (1048576 by default). Keep the soft limit at 1024 for programs that still use select(2), which cannot handle descriptor numbers above 1023. Size it from measured peak usage plus headroom, not from the largest number that works.

The fix in order

  1. Read /proc/<pid>/limits on the actual running process. Ignore your shell's ulimit.
  2. Count fds with ls /proc/<pid>/fd | wc -l and break them down with lsof -p. Decide leak vs legitimate load.
  3. If it is real load, set the limit where the process starts: LimitNOFILE= for systemd, limits.conf for PAM logins, ulimit -n for hand-run scripts.
  4. Restart the process and re-check /proc/<pid>/limits. Config that is not reflected on the live PID did nothing.
  5. If it is a leak, fix the pool or the missing close. The kernel limit is a fuse, not a solution.

The call we would make on a paging alert: read the live limits, count the fds, and only reach for the config file once lsof proves the process needs the headroom rather than losing it.

Sources

Explore topics:Linux
React
Share this post
KU

Kiril Urbonas

AI Engineer

553 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.