On high-density multi-tenant servers, database hosts, and container platforms running Ubuntu, unconstrained disk input/output (I/O) from background maintenance tasks—such as nightly backup archives, log compression, or batch data ingestion—can saturate storage queues. When an intensive batch process exhausts drive bandwidth, high-priority interactive services (such as Nginx web servers or PostgreSQL databases) suffer from severe I/O starvation and elevated thread latency. Through Linux Control Groups version 2 (cgroups v2) and systemd’s native resource control directives, system administrators can enforce strict per-service read and write bandwidth limits (in bytes per second) and IOPS caps directly inside service unit files without requiring low-level block device manipulation.
Architecture of systemd I/O Throttling Under cgroups v2
Ubuntu 22.04 LTS and 24.04 LTS run unified cgroups v2 by default, providing unified multi-resource hierarchical management. Unlike legacy cgroups v1 (which often failed to account for buffered write caching), cgroups v2 tracks both synchronous direct I/O and asynchronous buffered write-back flushing. Systemd exposes block I/O control through dedicated directives:
- IOReadBandwidthMax / IOWriteBandwidthMax: Hard bandwidth clamps enforcing maximum read or write throughput in bytes per second (e.g.
20Mfor 20 megabytes per second) against specific block devices. - IOReadIOPSMax / IOWriteIOPSMax: Hard operational caps limiting the maximum number of read or write I/O operations per second against a designated disk.
- IOWeight: Proportional I/O scheduling weight (scale 1 to 10000, default 100) evaluated by the kernel BFQ (Budget Fair Queueing) scheduler when multiple services contend for disk throughput.
Identifying Target Block Device Major and Minor Numbers
Systemd requires disk I/O limits to be mapped to physical or virtual block device nodes (or device paths under /dev/disk/by-id/). To query the block device identifier hosting your filesystem, open the terminal and query lsblk:
# Query block devices and mount paths
lsblk -o NAME,MAJ:MIN,SIZE,TYPE,MOUNTPOINTS
Identify the root volume disk name (e.g. /dev/nvme0n1 or /dev/sda) or retrieve its persistent device path:
# Inspect persistent disk path
udevadm info --query=property --name=/dev/sda | grep DEVLINKS
Configuring I/O Bandwidth Clamps in Service Unit Drop-ins
Rather than editing vendor service unit files directly in /lib/systemd/system/, apply resource limits using systemd override drop-in files stored in /etc/systemd/system/<service>.service.d/.
To throttle a background backup service (e.g. restic-backup.service) to a maximum read bandwidth of 30 MB/s and write bandwidth of 15 MB/s on /dev/sda:
sudo systemctl edit restic-backup.service
Insert the following configuration directives into the override editor:
[Service]
# Enable accounting for I/O metrics
IOAccounting=yes
# Restrict maximum read and write bandwidth
IOReadBandwidthMax=/dev/sda 30M
IOWriteBandwidthMax=/dev/sda 15M
# Cap maximum IOPS to prevent queue saturation on SSDs
IOReadIOPSMax=/dev/sda 800
IOWriteIOPSMax=/dev/sda 400
# Assign lower proportional scheduling weight during contention
IOWeight=20
Enforcing Dynamic I/O Limits at Runtime via systemctl set-property
System administrators can dynamically apply or adjust disk I/O limits on running daemons without restarting the underlying process using systemctl set-property.
To clamp disk read bandwidth on an active indexing service immediately:
sudo systemctl set-property elasticsearch.service IOReadBandwidthMax="/dev/nvme0n1 50M"
To enforce the setting ephemerally without persisting it across reboots, append the --runtime switch:
sudo systemctl set-property --runtime rsync-archive.service IOWriteBandwidthMax="/dev/sda 10M"
Verifying Applied cgroup v2 Directives in sysfs
Systemd translates unit file directives directly into kernel cgroup configuration files located in the /sys/fs/cgroup/ pseudo-filesystem.
To confirm that the kernel has accepted the I/O limits for your service:
# Inspect the io.max file for the target service cgroup
cat /sys/fs/cgroup/system.slice/restic-backup.service/io.max
The command outputs the major:minor device numbers paired with byte and IOPS caps:
8:0 rbps=31457280 wbps=15728640 riops=800 wiops=400
Monitoring Live Service I/O Consumption via systemd-cgtop
To audit real-time disk read and write throughput across all active systemd services and slices, use the systemd-cgtop utility:
# Monitor cgroup I/O throughput in real time, ordered by disk write rate
sudo systemd-cgtop --order=io
Review the IO R/s and IO W/s columns to verify that the throttled service stays strictly within the configured bandwidth ceiling during heavy disk workloads.
By pairing cgroups v2 with systemd resource control directives in Ubuntu, systems engineers protect core application responsiveness, eliminate noisy neighbour bottlenecks, and achieve deterministic storage QoS.