Server

File descriptor

Also called ulimit.

File descriptor is the small integer a process gets from the operating system for each open file or socket. Each connection uses one, and every process has a limit set by `ulimit`.

How it is measured

Count open descriptors for a process with `ls /proc/<pid>/fd | wc -l` or `lsof -p <pid> | wc -l`, then compare with `ulimit -n` and the per-process limit in systemd. The ratio of used to allowed is the figure to track.

The default soft limit is often 1,024, which a busy Nginx or Node process can reach with a few hundred clients, since each uses one for the client and one for the upstream.

Worked example

An Nginx reverse proxy in front of a Node app has `worker_connections` set to 4,000 but the service inherits a limit of 1,024. At 480 simultaneous clients, with an upstream socket for each, it needs about 960 descriptors plus logs and certificates.

At 520 clients it runs out. Nginx logs 'accept() failed (24: Too many open files)' and visitors see resets. Raising LimitNOFILE to 65535 in the unit file and reloading fixed it.

How it differs

A file descriptor is the OS handle for an open file or socket. A connection pool is an application-level set of reusable database connections, and each one consumes a descriptor. The descriptor limit excludes any knowledge of what the handle is for. The pool limit excludes the process-wide ceiling that sits above it.

Common errors

Raising ulimit in a shell but not in the systemd unit. Leaking sockets by never closing responses. Reading the system-wide limit when the per-process one is the bottleneck. Ignoring descriptors used by log files and temp files. Setting a limit so high that a leak never gets noticed.

In practice

Check `cat /proc/<pid>/limits` for your web server and app process today. Graph open descriptors, set the limit well above peak, and alert at 80 percent so a leak shows up as a trend and not as an outage.

See also

Connection pool, Keep-alive timeout

Sources

Count this on a real site.

Watch my website