In contemporary Linux systems where systemd manages the cgroups hierarchy, traditional approaches to user resource limitation face significant obstacles. The previously suggested method of templating user-UID.slice
units proves ineffective due to unsupported functionality in current systemd versions.
With cgroups v2 becoming the default in most modern distributions (since systemd v230+), administrators need to adapt their resource limitation strategies. The key constraint lies in systemd's architecture where user slices are dynamically created at login time.
Here's a Python daemon solution that listens for login events and applies resource limits:
#!/usr/bin/env python3
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
import subprocess
def handle_user_new(uid, path):
slice_name = f"user-{uid}.slice"
subprocess.run([
"systemctl",
"set-property",
slice_name,
"CPUAccounting=yes",
"MemoryAccounting=yes",
"CPUQuota=50%",
"MemoryMax=2G"
], check=True)
DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
bus.add_signal_receiver(
handle_user_new,
signal_name="UserNew",
dbus_interface="org.freedesktop.login1.Manager",
path="/org/freedesktop/login1"
)
loop = GLib.MainLoop()
loop.run()
For more immediate effect without relying on a daemon, integrate with PAM using pam_exec
:
# /etc/pam.d/login
session optional pam_exec.so /usr/local/bin/set_user_limits.sh
Sample script:
#!/bin/bash
if [ "$PAM_TYPE" = "open_session" ]; then
systemctl set-property "user-${PAM_UID}.slice" \
CPUAccounting=yes \
MemoryAccounting=yes \
CPUQuota=75% \
MemoryMax=4G
fi
To verify limits are applied:
systemd-cgls
systemd-cgtop
cat /sys/fs/cgroup/user.slice/user-1000.slice/cpu.max
Important factors when implementing this solution:
- Daemon reliability (consider systemd unit for the monitor)
- Performance impact of frequent cgroup updates
- Interaction with existing cgroup configurations
- Logging for audit purposes
In contemporary Linux distributions using systemd (v232+), traditional cgroup approaches for per-user resource limitations face compatibility issues. The systemd's unified cgroup hierarchy (cgroup v2) renders older methods ineffective, particularly for user-based resource control.
Attempting to use templated slices like user-UID.slice
proves problematic due to systemd's architectural decisions. As documented in systemd issue #2556, this approach isn't natively supported in the current implementation.
The most reliable method involves monitoring systemd-logind's DBus signals. Here's a Python implementation that automatically applies limits upon user login:
#!/usr/bin/env python3
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
import subprocess
def handle_user_new(uid, object_path):
slice_name = f"user-{uid}.slice"
subprocess.run([
"systemctl", "set-property",
slice_name,
"CPUAccounting=true",
"MemoryAccounting=true",
"CPUQuota=150%",
"MemoryMax=4G"
], check=True)
DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
login_manager = bus.get_object(
"org.freedesktop.login1",
"/org/freedesktop/login1"
)
login_manager.connect_to_signal(
"UserNew",
handle_user_new,
dbus_interface="org.freedesktop.login1.Manager"
)
loop = GLib.MainLoop()
loop.run()
For enterprise environments, consider implementing these limits via PAM:
# /etc/pam.d/system-auth
session optional pam_exec.so /usr/local/bin/set_user_limits.sh
# set_user_limits.sh
#!/bin/bash
if [ "$PAM_TYPE" = "open_session" ]; then
uid=$(id -u "$PAM_USER")
systemctl set-property "user-${uid}.slice" \
CPUAccounting=true \
MemoryAccounting=true \
CPUQuota=200% \
MemoryHigh=6G \
MemoryMax=8G
fi
After implementation, verify your settings with:
systemd-cgtop
systemctl show user-1000.slice | grep -E "(CPU|Memory)"
cat /sys/fs/cgroup/user.slice/user-1000.slice/cpu.max
For fine-grained control, create custom slice units:
# /etc/systemd/system/user-resource-control.slice.d/90-limits.conf
[Slice]
CPUQuota=180%
MemoryHigh=4G
MemoryMax=6G
IOReadBandwidthMax=/dev/sda 10M