Set Per-User File Descriptor Limits with pam_limits
You will set a per-user limit on open file descriptors through PAM, then verify the soft and hard values in a newly created login session. The example uses the nofile item, with a soft limit of 4096 and a hard limit of 8192. Allow about fifteen minutes, plus time to create a fresh session.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide is for a Linux system using the installed Linux-PAM module. On this machine, libpam-modules is version 1.5.3-5ubuntu5.7. You need the target account name and root access to edit /etc/security/limits.d. Keep an existing privileged session open while testing: a broken PAM session configuration can prevent logins.
1. Confirm that the module is available
pam_limits is a PAM session module, not a standalone command. Check the module file and the package version without elevated privileges:
$ test -r /usr/lib/x86_64-linux-gnu/security/pam_limits.so && echo 'module is readable'
module is readable
$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules
libpam-modules:amd64 1.5.3-5ubuntu5.7
The module must also be present in the PAM session stack used by the login method you care about. Inspect the relevant file before editing a limit:
$ rg -n 'pam_limits\.so' /etc/pam.d
/etc/pam.d/sshd:40:session required pam_limits.so
Your output will vary. For an SSH login, check /etc/pam.d/sshd; for a local login, check /etc/pam.d/login. If there is no matching session line, a limits file alone will not apply to that service. Adding PAM rules is a security-sensitive change, so review the service's existing stack and keep a recovery login available.
2. Choose the account and inspect its current session
Replace APP_USER with an existing, non-root account. The account name in a limits file is literal, so do not leave the placeholder in the file:
$ APP_USER='replace-with-real-user'
$ getent passwd "$APP_USER"
replace-with-real-user:x:1001:1001::/home/replace-with-real-user:/bin/bash
Run these checks in the account's current shell if you can. They establish a baseline, but they do not prove what a new PAM session will receive:
$ ulimit -Sn
1048576
$ ulimit -Hn
1048576
The two values are the soft and hard RLIMIT_NOFILE values. A soft limit is the normal working limit. A process can raise it only within the hard limit, and an ordinary user cannot raise the hard limit. Existing shells keep their old values after you edit the configuration.
3. Add one focused limits.d file
Use a separate file rather than rewriting the vendor-managed /etc/security/limits.conf. The command below changes system state and needs elevated privileges:
$ sudo install -o root -g root -m 0644 /dev/null /etc/security/limits.d/90-app-user-nofile.conf
$ sudo sh -c 'printf "%s\n" "replace-with-real-user soft nofile 4096" "replace-with-real-user hard nofile 8192" > /etc/security/limits.d/90-app-user-nofile.conf'
Substitute the actual account in both lines. The four fields are domain, type, item and value. Here, the domain is the username, soft and hard select the two limit types, and nofile counts open file descriptors. Do not use * unless you really mean every matching user: wildcard and group limits do not apply to root, and an explicit username is clearer for this test.
Check the file before opening a new session:
$ sudo sed -n '1,5p' /etc/security/limits.d/90-app-user-nofile.conf
replace-with-real-user soft nofile 4096
replace-with-real-user hard nofile 8192
Keep the file name late in the alphabet so it is easy to identify. Linux-PAM reads /etc/security/limits.conf and then the *.conf files in /etc/security/limits.d in C-locale order. A later matching rule or another applicable rule can affect the result, so inspect the directory if the values are not what you expect.
4. Start a completely new PAM session
Sign out and back in through the service whose PAM stack you checked, or open a new SSH connection. Do not rely on a new terminal window inside the same desktop login. The module sets limits for a user session; it does not change existing processes and the settings are not global or permanent.
In the new session, verify both values:
$ ulimit -Sn
4096
$ ulimit -Hn
8192
$ prlimit --nofile $$
RESOURCE DESCRIPTION SOFT HARD UNITS
NOFILE max number of open files 4096 8192 files
The exact spacing and description from prlimit can differ. The useful result is soft 4096 and hard 8192. If you use sudo -iu APP_USER as a test, remember that it tests the sudo PAM service, not necessarily SSH or your display manager. Use the real service for final verification.
Checkpoint: if the new session has the old values, first confirm that the module is in that service's session stack, the username matches getent passwd, and the file has four whitespace-separated fields. Then search for another nofile rule. A shell started from an old session cannot demonstrate a new PAM result.
5. Understand the limit's boundary
This setting limits processes created in the session and inherited by their children. It does not configure a systemd service that starts independently of PAM. For a system service, use the service manager's resource controls and verify the service's own process limits; do not assume that a login limits file reaches it.
Likewise, nofile is a per-process descriptor limit, not a promise that the kernel can provide an unlimited number of files. The system-wide ceiling in /proc/sys/fs/nr_open also matters when setting an explicit hard value. The local kernel may reject a value outside its supported range. Use a value appropriate to the workload and test the application rather than copying a very large number.
Do not add set_all merely to make unspecified values look consistent. The installed manual warns that it takes unspecified limits from PID 1, and when PID 1 is systemd those values are not the kernel defaults. Leave that module option out unless you have a specific, tested reason to use it.
6. Undo the example safely
Removing the example file is a privileged, state-changing operation. First preserve a copy if you may need to compare it later:
$ sudo cp --preserve=all /etc/security/limits.d/90-app-user-nofile.conf /etc/security/limits.d/90-app-user-nofile.conf.bak
$ sudo rm /etc/security/limits.d/90-app-user-nofile.conf
The backup is optional and should not be left in limits.d with a .conf suffix, or it may be read as another configuration file. Start another fresh PAM session to confirm that the example no longer applies. Existing sessions retain their inherited limits until they exit.
If a login starts failing after a PAM edit, use the privileged recovery session you kept open, restore the last known-good file or remove the new limits file, and inspect the service logs. Do not close the only working administrative session while testing PAM.
Done means
- The installed
pam_limits.soand Linux-PAM package version were identified. - The target service's PAM session stack contains
pam_limits.so. - A username-specific
nofilerule is present in a readablelimits.dfile. - A genuinely new session reports soft 4096 and hard 8192.
- You know that old shells and independently started services are outside this verification.
- You have a recovery session and an undo path before changing PAM configuration.