Sample output
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63515
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 63515
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
Two lines here decide most outages. open files (-n) 1024 is the ceiling your server hits at a thousand concurrent connections, and core file size (-c) 0 is why the crash you are trying to debug left no core dump behind. Everything printed is the soft limit, which is the enforced one; the hard limit sitting above it is usually much higher and costs nothing to claim.
When you would reach for it
A service dies at a suspiciously round number of connections. A crash you can reproduce refuses to leave a core file. A build works on your laptop and fails in CI with “Too many open files”, or a test suite that opens a lot of temporary files passes alone and fails when run in parallel. These are all the same question: what budget was this process handed at birth, and by whom. ulimit -a answers it for your shell, and by inheritance for everything you launch from it.
Gotchas
- Setting a limit without
-Ssets both halves of the pair.ulimit -n 2048looks like it lowers the soft limit and quietly drops the hard limit to 2048 as well. That is a one-way door: a non-root process can lower a hard limit but never raise it, so the nextulimit -n 8192in that shell fails with “Operation not permitted” and the only fix is a new shell. Writeulimit -Sn 2048when you mean the soft one, which is almost always. - Order matters when you lower a hard limit deliberately. If the new hard value would land under the current soft value, the kernel rejects it as
EINVALand bash reports “cannot modify limit: Invalid argument”, which reads like a permission problem and is not one. Lower the soft limit first, then the hard one. - Exceeding the file size limit does not return an error, it sends a signal. Write past
-fand the process dies with SIGXFSZ and the shell prints “File size limit exceeded (core dumped)”. Code that carefully checks the return value of every write never sees it coming, and-fis measured in 1024-byte units in bash, soulimit -f 1means one kilobyte and not one byte. - Raising a limit in your shell does nothing for anything already running, including the service you are trying to fix. Under systemd the daemon’s limits come from
LimitNOFILE=in its unit file and your login shell is not in the picture; on macOS the system ceiling islaunchctl limit maxfilesand the default soft limit is 256, far lower than any Linux box. macOS also prints a different format, one line per limit with the letter first, as in-n: file descriptors 256.
Variants
$ for r in c f l n s t u v; do printf '%-3s soft=%-10s hard=%s\n' "-$r" "$(ulimit -S$r)" "$(ulimit -H$r)"; done
Both columns at once, which no single ulimit invocation prints. The gap between the two is free headroom you can claim with ulimit -S$r hard
$ prlimit --pid 1234
Read the limits of a process you did not start, Linux only, from util-linux. cat /proc/1234/limits gives the same pair of columns with no package required, and either one settles the argument about whether the running daemon actually picked up your config change
$ bash -c 'ulimit -Sn 64; exec ./import.sh'
Reproduce a descriptor exhaustion bug on purpose, in a subshell whose limits die with it. exec replaces the subshell so the script runs with no extra process in between, and your own shell keeps its original limit