Apply Session-Start Disk Quotas with pam_setquota
pam_setquota applies block and inode limits to a chosen UID range the moment a PAM session opens, no login script required. The examples use pam_setquota from Ubuntu's libpam-modules version 1.5.3-5ubuntu5.7, installed here as /usr/lib/x86_64-linux-gnu/security/pam_setquota.so.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 20 minutes, plus a maintenance window if the rule will affect a real login service. You need root access, a filesystem with quotas configured, and a test account whose UID you can identify. This module changes quota state during PAM session setup, so do not test it first on a production-wide range.
Checkpoint
The working configuration is the PAM line itself, not a standalone command. The module only runs when a service such as SSH opens a session.
1. Confirm the installed module
Start with read-only checks, no elevated privileges needed:
$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules:amd64
libpam-modules 1.5.3-5ubuntu5.7
$ test -r /usr/lib/x86_64-linux-gnu/security/pam_setquota.so && echo module-present
module-present
Your package version may differ. Keep the local manual page next to the package when checking a different release, option defaults and implementation details belong to the installed module. pam_setquota is a PAM module, so typing pam_setquota at a shell prompt is not a useful test, it just isn't that kind of program.
2. Choose a narrow UID range
Find the test account's numeric UID and home directory:
$ id --user testquota
1500
$ getent passwd testquota
testquota:x:1500:1500:Quota test account:/home/testquota:/bin/bash
Replace testquota with an account that already exists. The module defaults startuid to the system's UID_MIN setting, and defaults enduid to 0, which means open-ended (every UID at or above startuid), not empty. For a first test, set both ends to 1500 so a typo can't touch every ordinary user's quota.
Check which filesystem contains the home directory before choosing fs:
$ findmnt --target /home/testquota --output TARGET,SOURCE,FSTYPE
TARGET SOURCE FSTYPE
/home /dev/sda1 ext4
The output is host-specific. The module accepts a device file or mountpoint; if fs is omitted it looks for the filesystem containing the user's home directory. An explicit mountpoint is easier to audit, but it must identify the filesystem where quotas are actually enabled.
3. Decide the limits before editing PAM
Block limits are passed as quota blocks to quotactl(2), not a promise of decimal megabytes. Inode limits count files and directories. Set each soft and hard pair together: bsoftlimit with bhardlimit, and isoftlimit with ihardlimit.
This example allows 1,000 soft and 2,000 hard blocks, plus 1,000 soft and 2,000 hard inodes, for UID 1500 on /home:
bsoftlimit=1000 bhardlimit=2000 isoftlimit=1000 ihardlimit=2000 startuid=1500 enduid=1500 fs=/home
The numbers are deliberately small for a test, not a recommendation. Confirm your quota unit convention and desired capacity before using real values. Supplying only one half of either pair is an error and returns PAM_PERM_DENIED.
4. Back up the PAM file
Warning
A malformed or misplaced PAM session rule can prevent logins through that service. Keep an existing root shell or console session open while testing, and make a root-owned backup before changing the file.
# cp --preserve=all /etc/pam.d/sshd /etc/pam.d/sshd.before-pam-setquota
# ls -l /etc/pam.d/sshd /etc/pam.d/sshd.before-pam-setquota
Use the PAM file for the service that should apply the policy. The example uses SSH; pick another file if your sessions come from a different service, and don't add this line to every PAM service unless that scope is genuinely intentional.
5. Add one session rule
Edit the chosen file as root and add one line in its session section:
session required pam_setquota.so bsoftlimit=1000 bhardlimit=2000 isoftlimit=1000 ihardlimit=2000 startuid=1500 enduid=1500 fs=/home
The required control means the session stack records a failure while continuing through the remaining session modules; the service ultimately treats the session as failed if a required module failed. Place the rule where it fits the service's existing session policy, then inspect the exact saved line:
$ grep -nF 'pam_setquota.so' /etc/pam.d/sshd
42:session required pam_setquota.so bsoftlimit=1000 bhardlimit=2000 isoftlimit=1000 ihardlimit=2000 startuid=1500 enduid=1500 fs=/home
6. Test one new session
Open a new session as the selected account without closing your existing administrative session. For SSH, log in from a separate terminal:
$ ssh testquota@HOSTNAME
$ id --user
1500
PAM_SUCCESSmeans the module set the quota.PAM_IGNOREmeans the UID was outside the range, a quota already existed andoverwrite=1wasn't set, or no limits were configured.
A successful login alone does not prove the quota was applied, so inspect it with the host's quota reporting tool and check the service logs for module diagnostics too.
Tip
For a diagnostic run, temporarily add debug=1 to the rule, open one test session, then remove it again. The debug output includes the old and new quota and may expose account policy details in logs, so treat those logs as sensitive and don't leave debugging switched on.
7. Add broader ranges only after the test works
Once the exact-UID test is understood, widen the range deliberately. This one covers UIDs 2001 through 3000:
session required pam_setquota.so bsoftlimit=19000 bhardlimit=20000 isoftlimit=3000 ihardlimit=4000 startuid=2001 enduid=3000 fs=/dev/sda1
You can use multiple pam_setquota.so lines for different ranges. The last matching entry wins, so review ordering carefully. A later line with overwrite=1 can replace an earlier result, while leaving overwrite at its default of 0 preserves an existing quota instead of overwriting it.
Warning
Do not add overwrite=1 casually. The manual warns it removes the administrator's ability to maintain different per-user values with edquota(8) on that filesystem. To undo the PAM policy, remove or comment out the rule, restore the backup, then correct existing quotas separately with your normal quota administration process; removing the PAM line does not itself restore quotas already written.
Common failure checks
- No action. Confirm the test UID is inside
startuidthroughenduid. Withenduid=0, the range is open-ended abovestartuid. - Permission denied. Check that the filesystem or device exists, quota support is enabled, both members of the relevant limit pair are present, and the session service has the privilege needed to set quotas. Read the service's syslog or journal entry for the module's detailed reason.
- Existing values remain. This is expected with the default
overwrite=0. Decide whether preserving manual values is the desired policy before changing it. - All users are affected. Inspect every matching line and remember that
enduid=0is open-ended. Restore the PAM backup if the scope is wrong, then review any quotas already changed.
Done means
- Module confirmed. The installed module and package version were checked.
- Scope chosen deliberately. The filesystem, test UID and block and inode pairs were chosen explicitly, not copied blindly.
- Backup exists. A backup exists and the rule sits in the intended service's
sessionstack. - Test session verified. A new test session matched the intended UID range and the resulting quota was checked.
overwrite=1is absent unless replacing existing per-user values is an intentional, documented policy.