EMFILE

Fix: "Too many open files" (EMFILE) on Linux & macOS

Hit EMFILE or "Too many open files"? Here's why the kernel blocks new file descriptors and the exact steps to raise the limit permanently.

Quick answer: Raise the per-process limit with ulimit -n (or LimitNOFILE in systemd), and bump the system-wide cap via fs.file-max — then restart the process, because a running one can't pick up the new value.

I know this error is infuriating. It shows up at the worst time — usually right when your app starts serving real traffic — and the stack trace just says EMFILE: too many open files with no hint about which file. Here's what's actually happening: every open file, socket, pipe, and epoll handle on Linux consumes a file descriptor. The kernel hands those out from a fixed pool. When your process asks for the next one past its limit, it gets errno 24 (EMFILE on Linux, also EMFILE on macOS) and the syscall fails. That's it. No mystery.

Two separate ceilings matter here. The per-process limit (soft and hard nofile) and the system-wide limit (fs.file-max on Linux, kern.maxfiles on macOS). If you only raise one, you'll still hit the wall — it just moves. I've watched teams double ulimit -n and then wonder why nginx still dies at 40k connections. The system cap was the real ceiling.

Common real-world triggers: a Node.js service using 1024 default descriptors under load, Elasticsearch with thousands of shards, nginx proxying a burst of WebSocket connections, or a Java app that forgets to close InputStream in a finally block and slowly leaks descriptors until it dies. That last one is nasty — the error appears hours after deploy, not seconds.

Check your current limits first

# Per-process soft and hard limits
ulimit -Sn
ulimit -Hn

# System wide (Linux)
cat /proc/sys/fs/file-max
cat /proc/sys/fs/file-nr   # allocated, unused, max

# macOS
sysctl kern.maxfiles
sysctl kern.maxfilesperproc

If ulimit -Sn says 1024, that's your problem. Raise it.

Fix steps (Linux)

  1. Raise the per-user limit for the shell. Edit /etc/security/limits.conf (or drop a file in /etc/security/limits.d/):
# /etc/security/limits.d/99-nofile.conf
*    soft    nofile    65535
*    hard    nofile    65535
root soft    nofile    65535
root hard    nofile    65535
  1. If the process runs under systemd — and most do on modern distros — limits.conf is ignored. Set it in the unit:
# /etc/systemd/system/myapp.service
[Service]
LimitNOFILE=65535

Then systemctl daemon-reload && systemctl restart myapp. I can't stress this enough: restarting is mandatory. The limit is copied into the process at fork time. Editing the file does nothing to a running daemon.

  1. Raise the system-wide ceiling so 65535 per process doesn't collapse when 200 processes hit the cap:
# /etc/sysctl.d/99-file-max.conf
fs.file-max = 2097152

# apply
sysctl --system
  1. Verify the running process actually got the new value. Don't trust ulimit in your login shell — the daemon has its own.
PID=$(pgrep -f myapp | head -1)
cat /proc/$PID/limits | grep -i 'open files'

# Count current open descriptors
ls /proc/$PID/fd | wc -l

If the process is already near the new limit, you don't have a limit problem — you have a leak. More on that below.

macOS specifics

macOS is weirder. ulimit -n works in a terminal session, but GUI-launched apps and launchd services ignore it. You need sysctl for the system cap and a launchd plist for the per-process value:

# /Library/LaunchDaemons/limit.maxfiles.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>limit.maxfiles</string>
  <key>ProgramArguments</key>
  <array><string>launchctl</string><string>limit</string>
  <string>maxfiles</string><string>65536</string><string>200000</string></array>
  <key>RunAtLoad</key><true/>
</dict>
</plist>

Load it with sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist. Reboot or restart the target service. This trips up almost everyone doing local dev on a Mac who then deploys to Linux — the numbers aren't the same.

If it still fails

  • Check for a leak. A flat ls /proc/$PID/fd | wc -l over 30 minutes tells you. If it climbs steadily, you're not closing descriptors. Grep the code for open(, Socket, FileInputStream, fs.createReadStream and look for missing finally/with/defer.
  • Watch for TIME_WAIT sockets. ss -s shows the count. Tens of thousands of short-lived outgoing connections will eat descriptors fast. Enable HTTP keep-alive instead of opening a new socket per request.
  • Container limits. Docker and Kubernetes each have their own caps. In Kubernetes, set the pod's ulimits via the container spec, not the host's limits.conf. Docker: --ulimit nofile=65535:65535.
  • inotify watch limits throw a different error (ENOSPC), but they're confused with EMFILE constantly. If you're running a file watcher like webpack or nodemon, check /proc/sys/fs/inotify/max_user_watches instead.

Prevention

Bake the limit into your deployment, not your muscle memory. Ship a systemd unit with LimitNOFILE=65535, add fs.file-max to a sysctl.d file, and monitor descriptor counts in your metrics stack — Prometheus's process_open_fds is a one-line addition to almost any exporter. The teams that never see EMFILE in production are the ones who set the ceiling once and alert on usage crossing 70% of it. Everything else is whack-a-mole.

Related Errors in Linux & Unix
Linux Redirect Page Not Working: Fix 301/302 Headers in Nginx and Apache Fix Kernel Panic: VFS Unable to Mount Root Filesystem Why df and du show different sizes than your GUI file manager systemctl status exited with code 1 Systemd Service Won't Start – Check These 3 Things First

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.