← Blog · Troubleshooting · August 7, 2026 · 6 min read · By Operate Engineering Staff

Too Many Open Files Is Almost Never a ulimit Problem: A Production Field Guide to File Descriptor Exhaustion

Too many open files is rarely a limit problem. How to tell a descriptor leak from a sizing or latency issue, across Linux, nginx, Postgres and Kubernetes.

Too Many Open Files Is Almost Never a ulimit Problem: A Production Field Guide to File Descriptor Exhaustion

Too Many Open Files Is Almost Never a ulimit Problem: A Production Field Guide to File Descriptor Exhaustion

TL;DR: The "too many open files" error occurs when a process or system hits its allocated limit for file descriptors, which include sockets, pipes, and files. Fixing it requires distinguishing between a resource leak, a traffic-driven sizing issue, or a latency-induced spike before blindly raising limits that may not even apply to your specific service runtime.

In production environments, encountering too many open files is a rite of passage for on-call engineers. Most guides suggest a quick ulimit -n adjustment, but for a distributed system, this rarely addresses the root cause. This field guide moves beyond the snippet to help you diagnose why your descriptors are exhausted and why your configuration changes might be failing.

The error, precisely

The error usually manifests as errno 24 too many open files (EMFILE) or too many open files in system (ENFILE). While they look similar, they indicate different exhaustion points. EMFILE means a single process has hit its per-process limit. ENFILE means the entire operating system has exhausted its total allocation of descriptors—a much more severe state that can freeze the host.

A "file" in Linux is a broad abstraction. When you see too many open files linux errors, you aren't just looking at text files on a disk. File descriptors (FDs) represent:

The error message is identical whether you have a genuine leak of 1,000 handles or you simply sized a high-traffic gateway too small.

The three shapes of descriptor growth

To fix the issue, you must identify the "shape" of the exhaustion.

Shape Behavior Root Cause First Action
Leak Count rises steadily under flat traffic; never falls. Unclosed HTTP bodies, DB connections, or file handles. Sample /proc/PID/fd for duplicates.
Sizing Count tracks traffic volume; falls when traffic drops. Concurrency limit is lower than peak legitimate demand. Raise LimitNOFILE in systemd.
Latency Count spikes suddenly when traffic is stable. Downstream dependency slowed down, causing requests to pile up. Check downstream p99 latency.

Key Takeaway: Before you touch a limit, sample the descriptor count over time against traffic: if it rises under flat traffic you have a leak, if it tracks traffic you have a sizing decision, if it spikes when a dependency slows you have a latency problem.

The limit you set is probably not the limit the process got

One of the most common frustrations is seeing too many open files ubuntu or CentOS errors persist after running ulimit -n. This happens because ulimit only affects the current shell session and its children.

Diagnose in five commands

If you are mid-incident, run these five commands to locate the pressure point:

  1. Check the count: ls /proc/PID/fd | wc -l Sample this every 30 seconds. A positive slope under flat traffic confirms a leak.
  2. Identify the type: lsof -p PID | awk '{print $5}' | sort | uniq -c This shows if your descriptors are REG (files), IPv4/v6 (sockets), or unix (local pipes).
  3. Check socket states: ss -tan | grep PID A high volume of CLOSE_WAIT indicates an application bug where the code is not calling close(). TIME_WAIT usually indicates high connection churn.
  4. Find "ghost" files: lsof -p PID | grep '(deleted)' This identifies files that were deleted from disk (like a rotated log) but are still held open, consuming space and descriptors.
  5. Inspect the targets: ls -l /proc/PID/fd This provides the actual paths or socket inodes for the descriptors, showing exactly what the app is "holding."

The same ceiling at every layer

The too many open files error propagates differently depending on your stack.

Little's Law is the model you are missing

Many engineers treat descriptors as a static resource, but they are a function of concurrency. According to Little's Law, L = λW (where L is concurrency, λ is arrival rate, and W is duration).

If your service handles 1,000 requests per second at a 50ms latency, it uses ~50 descriptors. If a downstream database slows down and latency jumps to 10 seconds, that same 1,000 rps now requires 10,000 descriptors. This is why socket too many open files errors often signal a latency crisis, not a configuration error.

Common pitfalls

Sources & further reading

Why this matters now

In an era of sidecars, microservices, and high-concurrency AI workloads, the surface area for descriptor exhaustion has expanded. When an incident occurs, the evidence—socket distributions, file handles, and slope data—is ephemeral.

This is where Operate changes the recovery flow. Descriptor exhaustion is a perfect example of a case where the evidence is on the box, spread across /proc, lsof output, socket states, and the traffic graph, and it is gone the moment someone restarts the service. Operate watches your production environment and, upon detecting an incident, automatically investigates by reading across code, database logs, and infrastructure metrics. It assembles the trail and produces a root cause with the evidence attached, verified by a second model. Instead of just suggesting a ulimit change, Operate can find the specific unclosed HTTP body in your PR history and draft a fix. Because it is read-only and self-hosted, your data stays private while you get the auditability needed to solve complex kernel-level failures.

First ten minutes: a checklist

  1. Check cat /proc/PID/limits to see the actual limit.
  2. Run ls /proc/PID/fd | wc -l three times to find the growth slope.
  3. Check ss -tan for a CLOSE_WAIT pile-up (indicates a code leak).
  4. Check downstream latency; is the "leak" just a pile-up of slow requests?
  5. If a restart is required, run lsof -p PID > fd_dump.txt first.
#sre#linux#troubleshooting#devops#performance