Skip to content

Limits and File Descriptors

Resource limits cap how many files a process may open and how many processes a user may run, and hitting them produces Too many open files or Resource temporarily unavailable. The limits come from three layers, and fixing the wrong one leaves the error in place.

Track: Core · Interview weight: High


Must-Know Facts

Fact Value Verify with
Soft vs hard Soft is the enforced limit; hard is the ceiling a user may raise the soft to ulimit -Sn, ulimit -Hn
Open files (-n) Per-process file descriptor limit; default soft often 1024 ulimit -n
EMFILE Too many open files: this process hit its -n limit /proc/PID/limits
ENFILE System-wide file table full (rare); governed by fs.file-max sysctl fs.file-max
file-nr Allocated, unused, and max open file handles system-wide cat /proc/sys/fs/file-nr
Processes (-u) RLIMIT_NPROC: max processes per real user id ulimit -u
EAGAIN on fork Resource temporarily unavailable: hit -u or pid_max ulimit -u
pid_max System-wide ceiling on process ids cat /proc/sys/kernel/pid_max
Shell limits ulimit sets the calling shell and its children ulimit -a
PAM limits /etc/security/limits.conf and limits.d for login sessions man limits.conf
systemd limits LimitNOFILE=, LimitNPROC= in the unit; ulimit does not reach services systemctl show -p LimitNOFILE UNIT
Live limits A running process shows its effective limits cat /proc/PID/limits

The Three Layers

A limit reaches a process through one of three paths, and they do not override each other. Interactive shells inherit from PAM (limits.conf), services inherit from systemd (LimitNOFILE=), and any process can lower its own soft limit with ulimit.

Set for Source Applies to
Login shells /etc/security/limits.conf, limits.d/* interactive and cron logins
systemd services LimitNOFILE=, LimitNPROC= in the unit or a drop-in daemons started by systemd
A shell and children ulimit -n, ulimit -u the current shell only

ulimit in a shell does not change a systemd service

A service started by systemd takes its limits from the unit, not from any shell. Raising ulimit -n in a terminal, or editing limits.conf, has no effect on nginx.service; set LimitNOFILE= in the unit instead.


Reading Current Limits

ulimit -a shows the calling shell's limits, and /proc/PID/limits shows what a running process actually has, which is the value that matters during an incident.

ulimit -a | grep -Ei 'open files|processes'
ulimit -Sn; ulimit -Hn
cat /proc/1/limits | grep -E 'Max open files|Max processes'

Output:

open files                          (-n) 1024
max user processes                  (-u) 3867
1024
524288
Max open files            1073741816           1073741816           files
Max processes             3867                 3867                 processes

The soft limit here is 1024 and the hard limit 524288, so a process may raise itself up to the hard ceiling without root. PID 1 (systemd) runs with a much higher file limit, which is why services can exceed a login shell's 1024.

Read the live limit from /proc, not from your own shell

ulimit -a shows your shell's limits, which need not match a running service. During an incident, read /proc/PID/limits for the actual process, since that is what the kernel enforces on it.


Too Many Open Files

When a process opens more descriptors than its soft -n allows, the next open fails with EMFILE. This is per-process, so the system-wide table is usually fine; file-nr confirms.

bash -c 'ulimit -n 12; exec python3 -c "
fs=[]
try:
    while True: fs.append(open(\"/etc/hostname\"))
except OSError as e: print(len(fs),\"opened, then:\",e)"'
cat /proc/sys/fs/file-nr

Output:

9 opened, then: [Errno 24] Too many open files: '/etc/hostname'
736 0   9223372036854775807

With -n set to 12, only nine files opened before the limit, because stdin, stdout, stderr and the interpreter's own descriptors used the rest. The system table (file-nr) shows 736 handles against an effectively unlimited maximum, so the fault is the per-process limit, not the system.


Raising the Limit

The fix depends on which layer applies. A leaking application that never closes descriptors needs a code fix, not a higher limit, but a legitimately busy service needs a real raise.

# For a service: a drop-in, then reload and restart
sudo systemctl edit nginx      # add [Service] LimitNOFILE=65536
sudo systemctl restart nginx
systemctl show nginx -p LimitNOFILE
# For login sessions: PAM limits
echo '* soft nofile 65536' | sudo tee /etc/security/limits.d/nofile.conf
echo '* hard nofile 65536' | sudo tee -a /etc/security/limits.d/nofile.conf
# log out and back in, then: ulimit -n

The systemd manager also has DefaultLimitNOFILE, which sets the fallback for every unit that does not specify its own.

systemctl show -p DefaultLimitNOFILE

Output:

DefaultLimitNOFILE=524288

Process Limits and fork

RLIMIT_NPROC (ulimit -u) caps processes per real user id, and pid_max caps the system total. Hitting either makes fork fail with EAGAIN, seen as Resource temporarily unavailable.

bash -c 'ulimit -u 50; exec python3 -c "
import os,sys
try:
    while True:
        if os.fork()==0: os._exit(0)
except OSError as e: sys.stderr.write(str(e))"'

Output:

[Errno 11] Resource temporarily unavailable

The same error at the shell reads bash: fork: retry: Resource temporarily unavailable. Because every fix normally needs a new process, recovery must use shell builtins, which run in the current shell, as Cannot Fork shows.


Common Errors

Too many open files

Cause: the process reached its soft -n limit, often through a descriptor leak or a genuinely high connection count.

Fix: count open descriptors with ls /proc/PID/fd | wc -l; raise LimitNOFILE (service) or limits.conf (login) if the count is legitimate, or fix the leak.

fork: retry: Resource temporarily unavailable

Cause: the user hit RLIMIT_NPROC, or the system hit pid_max; no new process can start.

Fix: find the runaway with ps -u USER --no-headers | wc -l; raise -u or kill the offenders using shell builtins only.


Interview Checkpoints

L1: What is the difference between a soft and a hard limit?

Say first: the soft limit is enforced now; the hard limit is the ceiling a process may raise its own soft limit to without root.

Proof: ulimit -Sn and ulimit -Hn show both; a non-root process can set soft up to hard, not beyond.

Follow-up: who can raise the hard limit? (root, or a unit's LimitNOFILE.)

L2: A service logs Too many open files. Walk through the fix.

Say first: confirm it is the per-process limit, count the descriptors, then raise the unit's limit or fix the leak.

Proof:

cat /proc/$(pgrep -n svc)/limits | grep 'open files'
ls /proc/$(pgrep -n svc)/fd | wc -l
sudo systemctl edit svc      # LimitNOFILE=65536

Follow-up: why did editing limits.conf not help this service? (systemd services ignore PAM limits.)

L2: You raised ulimit -n in your shell but the daemon still fails. Why?

Say first: the daemon is a systemd service and takes its limit from the unit, not from your shell.

Proof: systemctl show svc -p LimitNOFILE shows the real value; set it with LimitNOFILE= and restart.

Follow-up: where does the manager-wide default come from? (DefaultLimitNOFILE.)

L3: A box refuses new SSH sessions with resource temporarily unavailable. What is wrong?

Say first: a process or PID limit is exhausted, so fork fails and no new session can start.

Proof: check ulimit -u and count the user's processes; cat /proc/sys/kernel/pid_max and ps -eL | wc -l for the system total.

Follow-up: how do you investigate when you cannot even start a command? (shell builtins only: echo /proc/*/, printf, read.)

L4: Why does the file descriptor limit exist per process rather than only system-wide?

Say first: each descriptor consumes a kernel entry in the process table, and a per-process cap contains one runaway process without exhausting the whole system.

Proof: /proc/PID/limits is per process; fs.file-max and file-nr are the system ceiling and current use.

Don't say: that raising fs.file-max fixes a Too many open files error, which is almost always the per-process -n.

L4: Why is a file descriptor limit enforced per process rather than only system-wide?

Say first: each descriptor costs a kernel table entry, and a per-process cap contains one runaway process without letting it exhaust the whole system.

Proof: /proc/PID/limits is per process; fs.file-max and file-nr are the system ceiling and current use.

Don't say: that raising fs.file-max fixes a per-process Too many open files.


Captured on Rocky Linux 10.2 on an iximiuz Labs FlexBox microVM, kernel 6.1.167, 2026-09.