Every Linux process has resource caps: number of open files, number of processes, memory size. Without a safeguard, an application that leaks descriptors or a loop that creates processes endlessly can saturate the whole machine. This guide shows how to read these limits, set lasting ones per user with limits.conf and per service with systemd, and above all diagnose the classic trap: a limit set somewhere is not the one that applies. For intermediate administrators, every distribution. The LFCS objectives make it an explicit objective of the Users and Groups domain: "Configure user resource limits". The outputs come from tests on Debian 13, Ubuntu 24.04 and AlmaLinux 10, the systemd part from an AlmaLinux 10 VM.
What you will learn
- The difference between the soft limit and the hard limit.
- Read the limits in force with
ulimit,prlimitand/proc. - Set a lasting limit per user through
limits.conf. - Set a limit per service through the systemd
Limit*directives. - Understand why a limit does not apply where you expect it.
Why cap resources
The textbook case is the file descriptor leak: a server that opens connections without closing them ends up reaching its nofile cap and refuses every new client with a Too many open files, without crashing. The other is the fork bomb: a script that duplicates itself in a loop fills the process table until the machine becomes unusable. Capping nofile (open files) and nproc (processes per user) turns these incidents into a simple local refusal instead of a global collapse.
Each limit exists as two values. The soft limit is the one actually applied, which the user can raise up to the hard limit, the absolute ceiling they cannot exceed without privileges. You handle them with the ulimit builtin (-S for soft, -H for hard).
Read the limits in force
For the current session, ulimit -a lists everything; target one resource with its option (-n for nofile, -u for nproc). By default, an account often shows a soft limit of 1024 open files and a hard limit much higher:
ulimit -Sn # soft limit: 1024ulimit -Hn # hard limit: 524288For any process already running, the source of truth is the kernel itself, exposed in /proc/<pid>/limits, or read cleanly with prlimit:
prlimit --pid 1234 --nofilecat /proc/1234/limitsprlimit can also run a command under an imposed limit, handy to test how a program behaves under constraint. A single value sets both the soft and the hard limit: measured, prlimit --nofile=256 -- bash -c 'ulimit -Sn; ulimit -Hn' prints 256 twice. Write --nofile=256:4096 to tell them apart.
prlimit --nofile=256 -- myprogramLimit a user durably: limits.conf
A value set with ulimit disappears when the terminal closes. For a limit to survive reconnections, you declare it in /etc/security/limits.conf or, better, in a dedicated file under /etc/security/limits.d/. The pam_limits module applies them, at each session opening.
-
Write the rule in a
limits.dfile. Each line has four fields: the domain (a user, a group with@, or*for everyone), the type (softorhard), the item (nofile,nproc...) and the value./etc/security/limits.d/90-appuser.conf appuser soft nofile 2048appuser hard nofile 4096appuser soft nproc 50 -
Open a new session for the user: the rule is only read when a session opens, never on a session already active.
Terminal window runuser -l appuser -c 'ulimit -Sn; ulimit -Hn; ulimit -Su'The output confirms the intended values, and nothing else:
2048409650 -
Check that another account is not affected. The rule targets
appuser, sorootkeeps its default.Terminal window ulimit -Sn # still 1024 for root
Limit a service: the systemd directives
Since a service does not read limits.conf, its limit is declared in its unit, with the Limit* directives (LimitNOFILE, LimitNPROC, LimitMEMLOCK...). It is the right place for a web server or a database that needs many descriptors.
-
Declare the limit in the unit (or in an override file through
systemctl edit)./etc/systemd/system/demo-limit.service [Service]Type=simpleExecStart=/bin/sleep 600LimitNOFILE=1234 -
Reload and start the service.
Terminal window sudo systemctl daemon-reloadsudo systemctl start demo-limit.service -
Check the limit actually applied to the process, in
/proc, and the configured value, through systemd.Terminal window pid=$(systemctl show -p MainPID --value demo-limit.service)grep 'Max open files' /proc/$pid/limitssystemctl show -p LimitNOFILE -p LimitNOFILESoft demo-limit.serviceMax open files 1234 1234 filesLimitNOFILE=1234LimitNOFILESoft=1234
The value 1234 is indeed that of the process, regardless of any limits.conf. For a service, it is the only reliable way.
Check and troubleshoot
When a limit "does not work", the reflex is to compare what is configured with what is applied. The configuration is read in limits.d or in the systemd unit; the application is always read in /proc/<pid>/limits, the single source of truth. Here are the most frequent cases.
| Symptom | Likely cause | Solution |
|---|---|---|
A service stays at 1024 despite limits.conf | Services ignore limits.conf | Set LimitNOFILE= in the unit, daemon-reload, restart |
| The new limit is not taken into account | Session opened before the change | Reconnect; pam_limits only acts when a session opens |
ulimit: open files: cannot modify limit: Invalid argument | The user aims above their hard limit (measured: 700 for a hard of 600, whereas 600 passes) | Raise the hard value in limits.conf (requires root) |
prlimit --nofile=N also lowered the hard limit | A single value sets both | Write --nofile=soft:hard |
Put it into practice
A limit you believe is set and a limit actually applied are often two different values. On a VM, this lab makes you cap nofile for a user through a limits.d file, open a new session and compare the value of ulimit with what /proc reports.
Key points
- Each limit has a soft value (applied, adjustable) and a hard value (absolute ceiling).
ulimitacts on the current session;prlimitreads or imposes a limit on a process;/proc/<pid>/limitstells the truth.limits.conf(andlimits.d) sets lasting limits per user, applied bypam_limitsat every PAM session opening: login, SSH,su,sudo,runuser.- A systemd service ignores
limits.conf: its limit is set withLimitNOFILEand the like in the unit. - To diagnose, always compare the configured with the actually applied in
/proc.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Diagnose memory on Linux
A new full quiz with random questions
Work again only on the questions you missed