Configure systemd service resource limits and security isolation

Intermediate 35 min Apr 30, 2026 964 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure cgroups v2 resource limits and security isolation for systemd services to prevent resource exhaustion and improve system security. This tutorial covers memory, CPU, and I/O limits with monitoring and troubleshooting.

Prerequisites

  • Root or sudo access
  • Linux system with systemd and cgroups v2
  • Basic understanding of systemd services

What this solves

Systemd services without resource limits can consume excessive memory, CPU, or I/O, causing system instability or security vulnerabilities. This tutorial shows you how to configure cgroups v2 resource limits and security isolation for systemd services to prevent resource exhaustion, improve system stability, and enhance security through sandboxing.

Step-by-step configuration

Verify cgroups v2 support

Check that your system has cgroups v2 enabled, which is required for modern systemd resource limits.

cat /proc/filesystems | grep cgroup
cat /sys/fs/cgroup/cgroup.controllers

Create a test service for demonstration

Create a simple service to demonstrate resource limits and security features.

[Unit]
Description=Demo Application for Resource Limits
After=network.target

[Service]
Type=simple
User=nobody
Group=nogroup
ExecStart=/bin/bash -c 'while true; do echo "Demo app running"; sleep 10; done'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Configure memory limits

Set memory limits to prevent the service from consuming excessive RAM and causing system instability.

sudo mkdir -p /etc/systemd/system/demo-app.service.d
sudo tee /etc/systemd/system/demo-app.service.d/resource-limits.conf > /dev/null << 'EOF'
[Service]
# Memory limits
MemoryMax=512M
MemoryHigh=400M
MemorySwapMax=0

# Prevent OOM killer from killing other processes
OOMPolicy=stop
EOF

Configure CPU limits

Set CPU limits to prevent the service from monopolizing processor resources.

[Service]
# CPU limits
CPUQuota=50%
CPUWeight=100

# Nice priority (higher number = lower priority)
Nice=10

Configure I/O limits

Set I/O limits to prevent the service from saturating disk bandwidth and affecting other processes.

[Service]
# I/O limits (bytes per second)
IOReadBandwidthMax=/ 10M
IOWriteBandwidthMax=/ 5M

# I/O operations per second limits
IOReadIOPSMax=/ 1000
IOWriteIOPSMax=/ 500

# I/O scheduling class (0=none, 1=realtime, 2=best-effort, 3=idle)
IOSchedulingClass=2
IOSchedulingPriority=4

Configure basic security isolation

Apply security sandboxing to limit what the service can access on the system.

[Service]
# Filesystem access restrictions
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/demo-app
ReadOnlyPaths=/etc/demo-app

# Network restrictions
PrivateNetwork=false
RestrictAddressFamilies=AF_INET AF_INET6

# Process restrictions
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true

Configure advanced security features

Enable additional security features including capability restrictions and system call filtering.

[Service]
# Capability restrictions
CapabilityBoundingSet=
AmbientCapabilities=

# System call filtering
SystemCallFilter=@system-service
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM

# Memory and execution restrictions
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictSUIDSGID=true
RestrictNamespaces=true

# Hostname and clock restrictions
ProtectHostname=true
ProtectClock=true

Create required directories and set permissions

Create necessary directories for the service with proper ownership and permissions.

sudo mkdir -p /var/log/demo-app /etc/demo-app
sudo chown nobody:nogroup /var/log/demo-app
sudo chmod 755 /var/log/demo-app
sudo chmod 755 /etc/demo-app
Never use chmod 777. It gives every user on the system full access to your files. Instead, use specific ownership with chown and minimal permissions like 755 for directories.

Reload systemd and start the service

Reload systemd configuration to apply the new limits and start the service.

sudo systemctl daemon-reload
sudo systemctl enable demo-app.service
sudo systemctl start demo-app.service

Configure resource limits for existing services

Apply resource limits to critical system services like nginx or database services.

sudo mkdir -p /etc/systemd/system/nginx.service.d
sudo tee /etc/systemd/system/nginx.service.d/limits.conf > /dev/null << 'EOF'
[Service]
# Memory limits for nginx
MemoryMax=1G
MemoryHigh=800M

# CPU limits
CPUQuota=200%

# I/O limits
IOReadBandwidthMax=/ 50M
IOWriteBandwidthMax=/ 20M

# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/log/nginx /var/lib/nginx
EOF

Apply limits and restart services

Reload systemd and restart services to apply the new resource limits.

sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl restart demo-app

Monitor resource usage and troubleshooting

Monitor service resource usage

Use systemd tools to monitor how much resources your services are consuming.

# Check current resource usage
sudo systemctl status demo-app
sudo systemd-cgtop

# Detailed cgroup information
sudo systemctl show demo-app --property=MemoryCurrent,CPUUsageNSec,IOReadBytes,IOWriteBytes

# Monitor resource limits in real-time
sudo systemd-run --scope --slice=demo-app.slice systemd-cgtop

Check resource limit violations

Monitor the journal for resource limit violations and OOM events.

# Check for memory limit violations
sudo journalctl -u demo-app | grep -i "memory\|oom"

# Check for CPU throttling
sudo journalctl -u demo-app | grep -i "cpu\|throttl"

# Monitor all cgroup-related events
sudo journalctl -f | grep cgroup

Create monitoring scripts

Set up automated monitoring scripts to track resource usage and send alerts.

#!/bin/bash

SERVICE_NAME="demo-app"
MEMORY_THRESHOLD=80
CPU_THRESHOLD=90

# Get memory usage percentage
MEMORY_LIMIT=$(systemctl show $SERVICE_NAME --property=MemoryMax --value)
MEMORY_CURRENT=$(systemctl show $SERVICE_NAME --property=MemoryCurrent --value)

if [ "$MEMORY_LIMIT" != "infinity" ] && [ "$MEMORY_CURRENT" -gt 0 ]; then
    MEMORY_PERCENT=$((MEMORY_CURRENT * 100 / MEMORY_LIMIT))
    if [ "$MEMORY_PERCENT" -gt "$MEMORY_THRESHOLD" ]; then
        echo "WARNING: $SERVICE_NAME memory usage at ${MEMORY_PERCENT}%"
        logger -t resource-monitor "$SERVICE_NAME memory usage high: ${MEMORY_PERCENT}%"
    fi
fi

# Check for recent OOM events
if journalctl -u $SERVICE_NAME --since="5 minutes ago" | grep -q "oom-kill"; then
    echo "CRITICAL: $SERVICE_NAME was killed by OOM killer"
    logger -t resource-monitor "$SERVICE_NAME OOM kill detected"
fi

echo "Resource monitoring complete for $SERVICE_NAME"
sudo chmod 755 /usr/local/bin/monitor-service-resources.sh

Set up automated monitoring

Create a systemd timer to run resource monitoring automatically.

[Unit]
Description=Service Resource Monitor

[Service]
Type=oneshot
ExecStart=/usr/local/bin/monitor-service-resources.sh
User=root
[Unit]
Description=Run resource monitoring every 5 minutes
Requires=resource-monitor.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable --now resource-monitor.timer

Verify your setup

# Check service status and limits
sudo systemctl status demo-app
sudo systemctl show demo-app --property=MemoryMax,CPUQuota,IOReadBandwidthMax

# Verify cgroups v2 configuration
sudo cat /sys/fs/cgroup/system.slice/demo-app.service/memory.max
sudo cat /sys/fs/cgroup/system.slice/demo-app.service/cpu.max

# Test resource monitoring
sudo /usr/local/bin/monitor-service-resources.sh
sudo systemctl status resource-monitor.timer

# Check security restrictions
sudo systemd-analyze security demo-app

Common issues

SymptomCauseFix
Service fails to start after adding limitsResource limits too restrictiveIncrease limits in service drop-in files and reload: sudo systemctl daemon-reload
MemoryMax not workingcgroups v1 in useEnable cgroups v2: sudo grub-editenv - set "systemd.unified_cgroup_hierarchy=1" and reboot
Service killed by OOMMemory limit too lowCheck actual usage with systemd-cgtop and increase MemoryMax
CPU limits not effectiveCPUQuota misconfigurationUse percentage format: CPUQuota=50% for half a CPU core
I/O limits not workingWrong device specificationUse correct device: IOReadBandwidthMax=/dev/sda 10M instead of /
Security restrictions too strictService needs specific accessAdd required paths to ReadWritePaths= or ReadOnlyPaths=
Monitoring script failsPermission issuesEnsure script has execute permissions: sudo chmod 755 /usr/local/bin/monitor-service-resources.sh

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

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