How to Enforce CPU Quota Limits for Processes Using systemd’s CPUQuota Parameter


3 views

When attempting to limit CPU usage for a process via systemd's CPUQuota parameter, many administrators encounter unexpected behavior where processes exceed their allocated CPU percentage. Let's examine why the original implementation failed:

# Original unit file example
[Unit]
Description=Virtual Distributed Ethernet

[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%

[Install]
WantedBy=multi-user.target

CPUQuota actually specifies the maximum CPU time per second (in percentage) that a service can use across all CPU cores. The key points about its implementation:

  • Works at the cgroup level (specifically cpu.cfs_quota_us)
  • 10% means 100ms of CPU time per second
  • Requires CPUAccounting=true for proper measurement
  • Operates within the CPU CFS scheduler

Here's the proper way to configure CPU limiting for your dd process:

[Unit]
Description=CPU Limited dd Process

[Service]
CPUAccounting=true
CPUQuota=10%
ExecStart=/bin/sh -c "dd if=/dev/zero of=/dev/null bs=1024k"

# Optional but recommended for better control
MemoryAccounting=true
BlockIOAccounting=true

[Install]
WantedBy=multi-user.target

After applying these changes, verify with:

# Reload systemd
sudo systemctl daemon-reload

# Check current CPU usage
systemd-cgtop

# View detailed CPU metrics
systemctl show your-service-name | grep CPU

For more granular control, consider combining CPUQuota with:

[Service]
CPUAccounting=true
CPUQuota=10%
CPUShares=512
CPUWeight=50
AllowedCPUs=0-1  # Limit to specific cores

If CPU limits still aren't working:

  • Ensure systemd version > 231 (older versions have incomplete cgroupv2 support)
  • Check if cgroups are properly mounted: mount | grep cgroup
  • Verify kernel support: grep CGROUP /boot/config-$(uname -r)
  • Test with simpler commands first (e.g., yes > /dev/null)

When attempting to limit CPU usage for a dd process through systemd's CPUQuota parameter, you might encounter unexpected behavior where the process continues consuming 70-75% CPU despite being configured with CPUQuota=10%. This contradicts the expected behavior documented in systemd.resource-control.

The original unit file contains proper CPUQuota syntax:

[Unit]
Description=Virtual Distributed Ethernet

[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%

[Install]
WantedBy=multi-user.target

With the corresponding shell script:

#!/bin/sh
dd if=/dev/zero of=/dev/null bs=1024k

The systemctl show output reveals important details:

CPUShares=18446744073709551615
StartupCPUShares=18446744073709551615
CPUQuotaPerSecUSec=100ms
LimitCPU=18446744073709551615

The 100ms quota (equivalent to 10% of 1 second) is correctly set, but the unlimited LimitCPU value suggests potential conflicts with system-wide limits.

To properly investigate, check the actual cgroup settings:

systemd-cgls
cat /sys/fs/cgroup/cpu/system.slice/dd.service/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu/system.slice/dd.service/cpu.cfs_period_us

Try these modifications to ensure proper CPU limiting:

Option 1: Combine with CPUAccounting

[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
CPUAccounting=yes

Option 2: Explicit Period Configuration

[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
CPUQuotaPeriodSec=1s

Option 3: Alternative Limiting via cgroups

systemctl set-property dd.service CPUQuota=10% CPUAccounting=yes
systemctl daemon-reload
systemctl restart dd.service

If issues persist, consider these deeper investigations:

# Check for conflicts with other controllers
systemd-analyze dump | grep -A5 "dd.service"

# Verify kernel support
grep CFS /boot/config-$(uname -r)

# Monitor real-time CPU usage
systemd-cgtop -p -n 10

Here's a more robust test script to validate CPU limiting:

#!/bin/bash
# cpu_stress_test.sh
while true; do
    dd if=/dev/urandom of=/dev/null bs=1M count=1000
    sleep 0.1
done

Configure the unit file with explicit accounting:

[Service]
ExecStart=/usr/local/bin/cpu_stress_test.sh
CPUQuota=15%
CPUAccounting=yes
MemoryAccounting=yes
TasksAccounting=yes