Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Limit the resources of a process with ulimit and systemd

By updated

Improve this page

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, prlimit and /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:

Terminal window
ulimit -Sn # soft limit: 1024
ulimit -Hn # hard limit: 524288

For any process already running, the source of truth is the kernel itself, exposed in /proc/<pid>/limits, or read cleanly with prlimit:

Terminal window
prlimit --pid 1234 --nofile
cat /proc/1234/limits

prlimit 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.

Terminal window
prlimit --nofile=256 -- myprogram

Limit 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.

  1. Write the rule in a limits.d file. Each line has four fields: the domain (a user, a group with @, or * for everyone), the type (soft or hard), the item (nofile, nproc...) and the value.

    /etc/security/limits.d/90-appuser.conf
    appuser soft nofile 2048
    appuser hard nofile 4096
    appuser soft nproc 50
  2. 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:

    2048
    4096
    50
  3. Check that another account is not affected. The rule targets appuser, so root keeps 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.

  1. Declare the limit in the unit (or in an override file through systemctl edit).

    /etc/systemd/system/demo-limit.service
    [Service]
    Type=simple
    ExecStart=/bin/sleep 600
    LimitNOFILE=1234
  2. Reload and start the service.

    Terminal window
    sudo systemctl daemon-reload
    sudo systemctl start demo-limit.service
  3. 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/limits
    systemctl show -p LimitNOFILE -p LimitNOFILESoft demo-limit.service
    Max open files 1234 1234 files
    LimitNOFILE=1234
    LimitNOFILESoft=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.

SymptomLikely causeSolution
A service stays at 1024 despite limits.confServices ignore limits.confSet LimitNOFILE= in the unit, daemon-reload, restart
The new limit is not taken into accountSession opened before the changeReconnect; pam_limits only acts when a session opens
ulimit: open files: cannot modify limit: Invalid argumentThe 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 limitA single value sets bothWrite --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).
  • ulimit acts on the current session; prlimit reads or imposes a limit on a process; /proc/<pid>/limits tells the truth.
  • limits.conf (and limits.d) sets lasting limits per user, applied by pam_limits at every PAM session opening: login, SSH, su, sudo, runuser.
  • A systemd service ignores limits.conf: its limit is set with LimitNOFILE and 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

6 questions
6 min
70% to pass

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

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn