Skip to main content
RunBook Academy

LinuxXXXVII · Resource Managementulimits

ulimits and limits.conf - per-user resource limits

Foundation⏱ ~10 minulimit

What you'll learn

  • Read current ulimits
  • Set per-user limits via /etc/security/limits.conf
  • Distinguish soft and hard limits
  • Recognise when limits cause failures

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

ulimits are per-process resource limits: file descriptors, processes, CPU time, memory. This lesson covers how to set and apply them.

Read ulimits

ulimit -a

Output:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max resident set size   (kbytes, -m) unlimited
open files              (-n) 1024
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes               (-u) unlimited
virtual memory          (kbytes, -v) unlimited
file locks              (-x) unlimited

Set ulimits in shell

ulimit -n 65536    # open files
ulimit -u 4096     # max processes

These apply to the current shell. New processes inherit unless overridden.

Soft vs hard limits

Each limit has:

  • Soft limit: the actual enforced value. Processes can lower it further.
  • Hard limit: the maximum value. Only root can raise it.
ulimit -Hn   # hard limit (max)
ulimit -Sn   # soft limit (current)

/etc/security/limits.conf

For per-user persistent limits:

# /etc/security/limits.conf
*               soft    nofile          1024
*               hard    nofile          524288
deploy          soft    nproc           4096
deploy          hard    nproc           8192

* is the default entry — it applies to users that have no more specific entry. It is not a match-everything wildcard: man 5 limits.conf states that group and wildcard limits are not applied to the root user. Root needs its own lines, spelled with the literal username:

root            soft    nofile          1024
root            hard    nofile          524288

Leaving those out is the usual way a host ends up with root running on the compiled-in kernel defaults while every other account gets the intended values — and root is the account running the daemons you were trying to tune.

The first field also accepts a group with @group, and a UID range as min:max (@min:max for GIDs). Both are subject to the same root exclusion.

Note the shape: a modest soft limit and a generous hard limit. The hard limit is the ceiling a process is allowed to reach; the soft limit is what it starts with. A process that genuinely needs more descriptors raises its own soft limit with setrlimit() up to the hard limit — no root, no reboot, no login required. Handing every process a huge soft limit by default gains nothing and breaks things.

PAM integration

The limits are applied by PAM at login:

# /etc/pam.d/common-session
session required pam_limits.so

The wording matters: at PAM session establishment. That covers logins, su, and SSH sessions.

A systemd system service does not establish a PAM session unless its unit sets PAMName=, so limits.conf never applies to it. Whether that service’s user happens to have a login session open elsewhere is irrelevant — the limits are attached to the session, not to the user. This is the trap: an operator raises nofile in limits.conf, confirms it with ulimit -n in their own SSH session, and concludes the daemon got it too. It did not.

Set service limits with systemd directives, and check the running process rather than your shell:

systemctl show my-service -p LimitNOFILESoft -p LimitNOFILE
grep 'open files' /proc/$(systemctl show -p MainPID --value my-service)/limits

Common production limits

# /etc/security/limits.conf
*       soft    nofile     1024        # low; apps raise it via setrlimit()
*       hard    nofile     524288      # ceiling apps may raise to
*       soft    nproc      16384
*       hard    nproc      32768
root    soft    nofile     1024
root    hard    nofile     524288
root    soft    nproc      16384
root    hard    nproc      32768        # never 'unlimited' for root

nofile (file descriptors) is the most common limit to raise. Modern services handle many concurrent connections and may need 65536+ file descriptors — so give them a hard limit that allows it, and let them ask.

nproc (processes) is often hit by multi-threaded applications. 16384 is a reasonable default.

Verify what a process actually got — the lesson’s other examples all set limits, none of them check:

# Live process
cat /proc/$(pgrep -f nginx | head -1)/limits

# What systemd will hand a unit
systemctl show nginx -p LimitNOFILESoft -p LimitNOFILE

# Current shell
ulimit -Sn; ulimit -Hn

/proc/<pid>/limits is the only authoritative answer. ulimit -a in your shell tells you about your shell, not about the daemon.

systemd overrides

For systemd services, prefer systemd’s directives over limits.conf:

[Service]
LimitNOFILE=65536
LimitNPROC=16384

These are more reliable for services.

Common pitfalls

  • Limits don’t apply: the PAM module may not be loaded. Check /etc/pam.d/common-session.
  • Login session vs SSH session: limits.conf applies at PAM session start. SSH sessions that don’t go through PAM don’t get the limits.
  • Docker containers: container limits are set by Docker, not limits.conf. Use docker run --ulimit nofile=65536 ....

Knowledge check

Knowledge check · 5 questions

  1. Q1. What is the soft limit?

  2. Q2. Raising nofile in limits.conf changes nothing for a daemon started by systemd.

  3. Q3. Which of the following are valid ulimit types? Select all that apply.

  4. Q4. After adding `root soft nofile unlimited` to limits.conf, several daemons started by root hang for minutes at startup or are OOM-killed before they log anything. What is the mechanism?

  5. Q5. Setting `root hard nproc unlimited` is safe because root needs headroom.

Passing score: 75%. Answers are checked in this browser.