Technology · PRACTICAL GUIDE
Node.js EMFILE: find the descriptor-limit owner before raising ulimit
Capture the failing process, its inherited limit and live descriptor growth. Then separate a low ceiling from a leak, concurrency burst or watcher pressure.

EMFILE is an ownership problem before it is a tuning problem. A shell, a systemd service and a restarted process can have different evidence. Preserve the failing PID before a restart erases the strongest clues.
Freeze the failing process before restart
Record the exact command or service, parent launcher, PID, timestamp and first EMFILE stack line. Do not begin with a reboot or dependency reinstall: both can clear the descriptor table and hide whether the count was stable, rising or spiking.
Read the limit the process actually inherited
On Linux, read /proc/PID/limits for Max open files and compare it with the interactive shell's ulimit -Sn and -Hn. A child inherits resource limits from its parent; a shell change made after systemd launched a service does not retroactively change that service.
Measure live descriptors and growth
Count entries in /proc/PID/fd and sample more than once. Group symbolic-link targets into regular files, sockets, pipes and anonymous descriptors. A count pinned near a stable ceiling supports a limit branch; steady growth supports a leak; a short spike tied to parallel work supports a concurrency branch.
Separate watcher pressure from ordinary files
A stack ending in fs.watch or FSEvent marks a watcher surface, not automatic proof of one universal fix. Compare watched scope, ignored directories and process descriptor count. On Linux also distinguish per-process descriptors from separate inotify accounting before changing either boundary.
Change the correct owner once
If the service inherited an unnecessarily low soft limit, use its launcher or unit configuration and restart that service. If the count grows without returning, close the leaked file, socket, timer or watcher. If a burst causes the peak, bound concurrency and close promptly. Do not raise an unrelated shell limit and call the incident fixed.
Prove completion under the original launcher
Rerun the same workload from the same shell, supervisor, CI runner or service unit. Capture the new PID limit, peak descriptor count and post-workload baseline. Completion means EMFILE no longer occurs and the descriptor count returns to an explained steady state—not merely that one restart passed.
