Configure Linux user session limits with systemd and pam_limits for resource management

Intermediate 25 min Apr 03, 2026 1,965 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up comprehensive user session resource limits using systemd and PAM to prevent resource exhaustion, fix 'too many open files' errors, and enforce memory, CPU, and process restrictions for better system stability.

Prerequisites

  • Root or sudo access
  • Basic knowledge of Linux system administration
  • Systemd-based Linux distribution

What this solves

Linux systems without proper resource limits can suffer from resource exhaustion when users or applications consume too much memory, open too many files, or spawn excessive processes. This tutorial shows you how to configure systemd user session limits and PAM limits to enforce resource quotas per user and group, preventing system instability and the common "too many open files" error.

Understanding Linux resource limits and PAM

Linux resource limits control how much system resources individual users and processes can consume. There are two main mechanisms: PAM (Pluggable Authentication Modules) limits that apply when users log in, and systemd user session limits that control resources for entire user sessions including all spawned processes.

PAM limits are configured in /etc/security/limits.conf and apply traditional ulimit restrictions. Systemd user limits use cgroups and provide more granular control over modern systemd-managed user sessions. Both work together to provide comprehensive resource management.

Note: Systemd user limits require systemd 226+ and are automatically enabled on modern distributions. PAM limits work on all Linux systems but have more limited scope.

Step-by-step configuration

Install required packages

Install systemd development tools and PAM configuration utilities for managing user limits.

sudo apt update
sudo apt install -y systemd-container libpam-systemd
sudo dnf install -y systemd-container systemd-pam

Check current user limits

Examine existing resource limits for your user session to understand current restrictions.

ulimit -a
systemctl --user show --property=DefaultMemoryAccounting,DefaultTasksAccounting

Configure PAM limits

Set up traditional resource limits that apply when users log in through PAM authentication.

sudo cp /etc/security/limits.conf /etc/security/limits.conf.backup
# User limits configuration
# Format: 

# Limit open files (fixes "too many open files" error)
* soft nofile 65536
* hard nofile 65536

# Limit maximum processes per user
* soft nproc 32768
* hard nproc 32768

# Limit virtual memory (in KB)
* soft as unlimited
* hard as unlimited

# Limit core file size
* soft core 0
* hard core 0

# Specific limits for web server users
www-data soft nofile 100000
www-data hard nofile 100000
www-data soft nproc 16384
www-data hard nproc 16384

# Limits for database users
mysql soft nofile 65536
mysql hard nofile 65536
postgres soft nofile 65536
postgres hard nofile 65536

# Group-based limits for developers
@developers soft nofile 32768
@developers hard nofile 32768
@developers soft nproc 16384
@developers hard nproc 16384

Configure systemd user session limits

Create systemd configuration to control resources for entire user sessions using cgroups.

sudo mkdir -p /etc/systemd/user.conf.d
sudo mkdir -p /etc/systemd/system.conf.d
[Manager]
# Enable resource accounting for user sessions
DefaultMemoryAccounting=yes
DefaultTasksAccounting=yes
DefaultIOAccounting=yes
DefaultCPUAccounting=yes

# Default limits for user sessions
DefaultTasksMax=16384
DefaultMemoryHigh=4G
DefaultMemoryMax=8G
DefaultTimeoutStopSec=30s

Create user-specific systemd limits

Set up per-user resource limits using systemd user slice configuration files.

sudo mkdir -p /etc/systemd/system/user-.slice.d
[Slice]
# Memory limits for user sessions
MemoryAccounting=yes
MemoryHigh=2G
MemoryMax=4G

# Process limits
TasksAccounting=yes
TasksMax=8192

# CPU limits (percentage of one CPU core)
CPUAccounting=yes
CPUQuota=200%

# I/O limits
IOAccounting=yes
IODeviceLatencyTargetSec=/dev/sda 100ms

Configure application-specific limits

Create targeted limits for high-resource applications like web servers and databases.

sudo mkdir -p /etc/systemd/system/nginx.service.d
sudo mkdir -p /etc/systemd/system/mysql.service.d
[Service]
# Nginx resource limits
LimitNOFILE=100000
LimitNPROC=32768
MemoryHigh=1G
MemoryMax=2G
TasksMax=4096
[Service]
# MySQL resource limits
LimitNOFILE=65536
LimitNPROC=16384
MemoryHigh=2G
MemoryMax=4G
TasksMax=8192
OOMScoreAdjust=-600

Enable systemd resource accounting

Enable comprehensive resource tracking and accounting in systemd for better limit enforcement.

# Add these lines to the [Manager] section
DefaultMemoryAccounting=yes
DefaultTasksAccounting=yes
DefaultIOAccounting=yes
DefaultCPUAccounting=yes
DefaultBlockIOAccounting=yes

Configure session limits per user

Set up individual user resource limits using systemd user configuration overrides.

sudo mkdir -p /etc/systemd/system/user@1000.service.d
[Service]
# Limits for user ID 1000
MemoryHigh=1G
MemoryMax=2G
TasksMax=4096
CPUQuota=150%

# File descriptor limits
LimitNOFILE=32768
LimitNPROC=8192

Apply configuration changes

Reload systemd configuration and restart user sessions to apply the new resource limits.

sudo systemctl daemon-reload
sudo systemctl daemon-reexec

# Restart user manager for current user
systemctl --user daemon-reload
sudo systemctl restart systemd-logind

Configure PAM session limits

Ensure PAM properly applies limits during user login sessions.

sudo cp /etc/pam.d/common-session /etc/pam.d/common-session.backup
# Add this line to enable PAM limits
session required pam_limits.so

# Existing PAM session configuration
session [default=1] pam_permit.so
session requisite pam_deny.so
session required pam_permit.so
session optional pam_systemd.so

Monitor and troubleshoot resource limit issues

Monitor user resource usage

Use systemd tools to monitor current resource consumption and limit enforcement.

# View user session resource usage
systemctl status user@$(id -u).service
systemd-cgtop

# Check specific user slice resources
systemctl status user-$(id -u).slice

# Monitor file descriptor usage
lsof | wc -l
cat /proc/sys/fs/file-nr

Debug limit violations

Identify and troubleshoot processes that exceed configured resource limits.

# Check system resource limits
cat /proc/sys/kernel/pid_max
cat /proc/sys/fs/file-max

# View per-process limits
cat /proc/self/limits

# Check systemd limit violations in logs
sudo journalctl -u user@$(id -u).service | grep -i limit
sudo journalctl | grep -i "memory limit\|task limit\|file limit"

Create monitoring script

Set up automated monitoring for resource limit violations and usage patterns.

#!/bin/bash
# Resource limit monitoring script

LOG_FILE="/var/log/resource-limits.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

# Check open files
OPEN_FILES=$(lsof | wc -l)
FILE_LIMIT=$(ulimit -n)
FILE_USAGE=$(echo "scale=2; $OPEN_FILES * 100 / $FILE_LIMIT" | bc -l)

# Check processes
PROC_COUNT=$(ps aux | wc -l)
PROC_LIMIT=$(ulimit -u)
PROC_USAGE=$(echo "scale=2; $PROC_COUNT * 100 / $PROC_LIMIT" | bc -l)

# Log usage statistics
echo "$DATE - Files: ${OPEN_FILES}/${FILE_LIMIT} (${FILE_USAGE}%) Processes: ${PROC_COUNT}/${PROC_LIMIT} (${PROC_USAGE}%)" >> $LOG_FILE

# Alert if usage is high
if (( $(echo "$FILE_USAGE > 80" | bc -l) )); then
    echo "$DATE - WARNING: High file descriptor usage: $FILE_USAGE%" >> $LOG_FILE
fi

if (( $(echo "$PROC_USAGE > 80" | bc -l) )); then
    echo "$DATE - WARNING: High process usage: $PROC_USAGE%" >> $LOG_FILE
fi
sudo chmod +x /usr/local/bin/monitor-limits.sh

# Add to crontab for regular monitoring
echo "*/5 * * * * /usr/local/bin/monitor-limits.sh" | sudo crontab -

Verify your setup

Test that resource limits are properly configured and enforced for user sessions.

# Check current user limits
ulimit -a

# Verify systemd user session limits
systemctl --user show --property=MemoryHigh,MemoryMax,TasksMax
systemctl status user-$(id -u).slice

# Test file descriptor limits
ulimit -n
echo "File descriptor limit: $(ulimit -n)"

# Check PAM limits are loaded
sudo grep pam_limits /etc/pam.d/common-session

# Verify systemd resource accounting
systemctl show user@$(id -u).service | grep Accounting

# Test process limits
ulimit -u
echo "Process limit: $(ulimit -u)"

You should see your configured limits reflected in the ulimit output and systemd status commands. The file descriptor limit should show 65536 and process limits should match your configuration.

Common issues

Symptom Cause Fix
"Too many open files" error nofile limit too low Increase nofile in limits.conf and restart application
Limits not applied after login PAM limits not enabled Add pam_limits.so to /etc/pam.d/common-session
Systemd limits ignored Resource accounting disabled Enable accounting in systemd configuration files
Memory limits not enforced No MemoryMax set Add MemoryMax to user slice or service configuration
Process spawn failures TasksMax too restrictive Increase TasksMax in systemd user configuration
Service won't start Conflicting limit configurations Check systemctl status and journal logs for errors
Warning: Setting limits too restrictively can cause application failures. Test limits gradually and monitor system behavior before applying to production systems.

Next steps

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 01:28 · reachable in a message, no ticket form