Technotes

Technotes for future me

ulimit

This cheat sheet provides a systematic way to debug problems with system limits. Follow those steps and you can be sure that you find the problem or prove that there is no limit related problem.

1. Do not rely on ulimit anymore!

This is the most important takeaway. Given that modern init systems (systemd, upstart, start-stop-daemon) do not care about /etc/security/limits.conf anymore it is not safe to rely on ulimit anymore!

No matter what

ulimit -a

and

cat /etc/security/limits.conf
cat /etc/security/limits.d/*.conf

might still tell you it affects only process you directly start from your shell.

As soon as you use a command like

systemctl <name> restart
service restart <name> 
/etc/init.d/<name> restart

those limits probably won’t have any effect.

2. Always Check Effective Limit using prlimit

Check the per-process limit using

prlimit --pid=<pid>

or

cat /proc/<pid>/limits

prlimit has the more readable output:

RESOURCE   DESCRIPTION                             SOFT      HARD UNITS
AS         address space limit                unlimited unlimited Bytes
CORE       max core file size                         0 unlimited blocks
CPU        CPU time                           unlimited unlimited seconds
DATA       max data size                      unlimited unlimited Bytes
FSIZE      max file size                      unlimited unlimited blocks
LOCKS      max number of file locks held      unlimited unlimited 
MEMLOCK    max locked-in-memory address space     65536     65536 Bytes
MSGQUEUE   max bytes in POSIX mqueues            819200    819200 Bytes
NICE       max nice prio allowed to raise             0         0 
NOFILE     max number of open files               65536     65536 
NPROC      max number of processes                15072     15072 
RSS        max resident set size              unlimited unlimited pages
RTPRIO     max real-time priority                     0         0 
RTTIME     timeout for real-time tasks        unlimited unlimited microsecs
SIGPENDING max number of pending signals          15072     15072 
STACK      max stack size                       8388608 unlimited Bytes

Note: Running “prlimit” or “ulimit -a” in the shell of the respective user rarely tells something because the init daemon responsible for launching services might be ignoring /etc/security/limits.conf as this is a configuration file for PAM only and is applied on login only per default.

Live update limits with prlimit

If you identified processes with a limit issue you can increase the limits without restarting by using prlimit. Here are some examples

prlimit --pid <pid> --nofile=15000:15000        # Increase nr of files to 15000
prlimit --pid <pid> --core=100000000:100000000  # Allow ~100MB core dump

General syntax is

prlimit --pid <pid> --<resource>=<soft limit>:<hard limit>

3. Always Check Global File Limit

If you suspect a limit hit on a system with many processes also check the global limit:

$ cat /proc/sys/fs/file-nr
7488    0   384224

The first number is the number of all open files of all processes, the third is the maximum. If you need to increase the maximum run:

# sysctl -w fs.file-max=500000

Ensure to persist this in /etc/sysctl.conf to not loose the setting on next reboot.

4. Check “nofile” Per Process

Just checking the number of files per process often helps to identify bottlenecks. For every process you can count open files from using lsof:

lsof -n -p <pid> | wc -l

So a quick check on a burning system might be:

lsof -n 2>/dev/null | awk '{print $1 " (PID " $2 ")"}' | sort | uniq -c | sort -nr | head -25

which returns the top 25 file descriptor “eating” processes

 139 mysqld (PID 2046)
 105 httpd2-pr (PID 25956)
 105 httpd2-pr (PID 24384)
 105 httpd2-pr (PID 24377)
 105 httpd2-pr (PID 24301)
 105 httpd2-pr (PID 24294)
 105 httpd2-pr (PID 24239)
 105 httpd2-pr (PID 24120)
 105 httpd2-pr (PID 24029)
 105 httpd2-pr (PID 23714)
 104 httpd2-pr (PID 3206)
 104 httpd2-pr (PID 26176)
 104 httpd2-pr (PID 26175)
 104 httpd2-pr (PID 26174)
 104 httpd2-pr (PID 25957)
 104 httpd2-pr (PID 24378)
 102 httpd2-pr (PID 32435)
 53 sshd (PID 25607)
 49 sshd (PID 25601)

The same more comfortable including the hard limit:

lsof -n 2>/dev/null | awk '{print $1,$2}' | sort | uniq -c | sort -nr | head -25 | while read nr name pid ; do printf "%10d / %-10d %-15s (PID %5s)\n" $nr $(cat /proc/$pid/limits | grep 'open files' | awk '{print $5}') $name $pid; done

returns

 105 / 1024 httpd2-pr (PID 5368)
 105 / 1024 httpd2-pr (PID 3834)
 105 / 1024 httpd2-pr (PID 3407)
 104 / 1024 httpd2-pr (PID 5392)
 104 / 1024 httpd2-pr (PID 5378)
 104 / 1024 httpd2-pr (PID 5377)
 104 / 1024 httpd2-pr (PID 4035)
 104 / 1024 httpd2-pr (PID 4034)
 104 / 1024 httpd2-pr (PID 3999)
 104 / 1024 httpd2-pr (PID 3902)
 104 / 1024 httpd2-pr (PID 3859)
 104 / 1024 httpd2-pr (PID 3206)
 102 / 1024 httpd2-pr (PID 32435)
 55 / 1024 mysqld (PID 2046)
 53 / 1024 sshd (PID 25607)
 49 / 1024 sshd (PID 25601)
 46 / 1024 dovecot-a (PID 1869)
 42 / 1024 python (PID 1850)
 41 / 1048576 named (PID 3130)
 40 / 1024 su (PID 25855)
 40 / 1024 sendmail (PID 3172)
 40 / 1024 dovecot-a (PID 14057)
 35 / 1024 sshd (PID 3160)
 34 / 1024 saslauthd (PID 3156)
 34 / 1024 saslauthd (PID 3146)

5. Consider Typical Pitfalls with Limits

Systemd ignores /etc/security/limits.conf

When using systemd /etc/security/limits.conf doesn’t apply anymore. Workaround: The only way to increase limits is in the systemd unit file. You can find out the name of the unit file using “systemctl status <service name>”. In the unit files [Service] section add a Limit line like below:

[Service]
...
LimitNOFILE=8092

The old ulimit names match to Limit<upper case limit name> fields and the value “unlimited” now is called “infinity”

Init Scripts with start-stop-daemon ignore /etc/security/limits.conf

A typical mistake is trying to set limits for a daemon starting by a Debian start script using “start-stop-daemon” which on modern distros just delegates to systemd or on older distros isn’t integrated with PAM and for both reasons isn’t affected by /etc/security/limits.conf.

Workaround:

  • On systemd: Set Limit in the systemd unit file
  • Otherwise: Set the limits manually in the start script.

Upstart ignores /etc/security/limits.conf

On older Ubuntu releases services are started by upstart which similar to systemd ignores /etc/security/limits.conf. To get upstart to change the limits of a managed service you need to insert a line like

limit nofile 10000 20000

into the upstart job file in /etc/init.

Special Debian Apache Handling

The Debian Apache package which is also included in Ubuntu has a separate way of configuring “nofile” limits. If you run the default Apache in 12.04 and check /proc/<pid>/limits of a Apache process you’ll find it is allowing 8192 open file handles. No matter what you configured elsewhere. This is because Apache defaults to 8192 files. If you want another setting for “nofile” then you need to edit /etc/apache2/envvars.

Login Session Workarounds

When changing limits.conf re-login!

After you apply a change to /etc/security/limits.conf you need to login again to have the change applied to your next shell instance by PAM. Alternatively you can use sudo -i to switch to user whose limits you modified and simulate a login.

Last updated on 31 Jan 2021
Published on 25 Dec 2019
Edit on GitHub