I/O priority determines which processes get preferential access to disk bandwidth when multiple processes compete for storage throughput. On busy database servers running simultaneous backup jobs, log writes, and query workloads, unmanaged I/O priority can cause critical operations to stall behind bulk data transfers. This guide compares three approaches to Linux I/O priority management: ionice (per-process scheduling class), cgroup v2 io.weight (hierarchical I/O bandwidth allocation), and ioprio_set (programmatic syscall-based control).
Understanding Linux I/O Scheduling Priority
Linux uses the Completely Fair Queuing (CFQ) I/O scheduler (or its modern successors like BFQ and mq-deadline) to manage disk access. Each process is assigned an I/O scheduling class and priority level that determines its position in the disk request queue.
There are three I/O scheduling classes:
| Class | Value | Description |
|---|---|---|
| Idle | 3 | Only gets I/O time when no other process needs it |
| Best Effort | 2 | Default class, priority levels 0-7 |
| Real Time | 1 | Highest priority, gets I/O before Best Effort processes |
| Feature | ionice | cgroup v2 io.weight | ioprio_set syscall |
|---|---|---|---|
| Granularity | Per-process | Per-cgroup hierarchy | Per-thread |
| Scheduling | Class + priority level | Weighted proportional | Class + priority level |
| Persistence | Per-launch | Persistent via cgroup config | Per-call |
| Container Support | Manual per-process | Native Docker/K8s support | Programmatic |
| Hierarchical Control | No | Yes | No |
| BFQ Compatible | Yes | Yes (io.weight) | Yes |
| Setup Complexity | Low | Medium | High (requires coding) |
| Kernel Version | 2.6.13+ | 5.0+ (cgroup v2) | 2.6.13+ |
ionice — Per-Process I/O Priority
ionice sets or retrieves the I/O scheduling class and priority for processes. It is part of the util-linux package and works with any I/O scheduler that supports priority levels.
Installation
| |
Basic Usage
| |
Common I/O Priority Patterns
| |
Docker Compose with ionice
| |
cgroup v2 io.weight — Hierarchical I/O Bandwidth Allocation
cgroup v2 introduces io.weight for proportional I/O bandwidth distribution across process groups. Unlike ionice which sets absolute priority levels, io.weight allocates I/O bandwidth proportionally based on weight values (1 to 10000, default 100).
Setting Up cgroup v2 I/O Control
| |
io.weight vs io.max
cgroup v2 provides two complementary I/O controls:
| |
Docker Native cgroup I/O Control
Docker supports cgroup v2 I/O limits natively:
| |
Systemd Service I/O Weight
systemd integrates with cgroup v2 for per-service I/O weighting:
| |
| |
ioprio_set — Programmatic I/O Priority Control
The ioprio_set syscall provides programmatic control over I/O priority from within applications. It is the underlying mechanism that ionice uses, but calling it directly from code gives you dynamic priority adjustment based on runtime conditions.
Using ioprio_set from C
| |
Compile and run:
| |
Using ioprio_set from Python
| |
When to Use ioprio_set Directly
Direct syscall access is useful when:
- Your application needs to adjust I/O priority dynamically based on workload phase
- You are building a custom I/O scheduler or resource manager
- You need per-thread I/O priority (ionice works per-process only)
- You are developing a database engine with adaptive I/O scheduling
Why Self-Host and Manage I/O Priority Locally?
Managing I/O priority on your own servers gives you fine-grained control over disk bandwidth allocation that shared cloud storage cannot provide. Cloud block storage typically uses QoS tiers (standard, premium, ultra) that apply uniformly to all processes on the instance.
On bare-metal servers with local NVMe or SAS storage, I/O priority management ensures that critical database writes are never delayed by bulk backup operations. Log-intensive applications benefit from assigning low I/O priority to log compression and archival tasks, keeping disk bandwidth available for active transaction processing.
For related storage optimization, see our Linux I/O Schedulers comparison for disk scheduler selection and Linux Compressed Swap guide for memory-I/O tradeoff management. Our Linux Performance Profiling guide covers I/O bottleneck identification tools.
Choosing the Right I/O Priority Tool
| Scenario | Recommended Tool |
|---|---|
| Simple per-process priority | ionice |
| Container orchestration | cgroup v2 io.weight |
| Dynamic runtime adjustment | ioprio_set syscall |
| Systemd service management | IOWeight in service unit |
| Database vs backup separation | ionice (database RT, backup Idle) |
| Multi-tenant server | cgroup v2 io.weight per tenant |
| Custom application logic | ioprio_set from application code |
FAQ
What is the difference between ionice and ioprio_set?
ionice is a command-line wrapper around the ioprio_set syscall. ionice sets I/O priority for a process at launch or for an existing process by PID. ioprio_set is the underlying Linux syscall that can be called from any programming language, enabling dynamic priority changes from within running applications based on workload conditions.
Does cgroup v2 io.weight work with all I/O schedulers?
cgroup v2 io.weight works with the BFQ and mq-deadline I/O schedulers. It does not work with the none scheduler (typically used for NVMe devices that handle their own scheduling). To check your current scheduler: cat /sys/block/sda/queue/scheduler. If your device uses the none scheduler, use io.max for absolute limits instead of io.weight.
Can I set I/O priority for kernel threads?
Yes, but with limitations. ionice can set priority for kernel threads using the -p flag with the kernel thread PID. However, many kernel threads ignore user-set I/O priority and use their own internal scheduling. For cgroup v2, moving kernel threads to a specific cgroup (via cgroup.procs) applies the cgroup I/O policy to them.
What happens to I/O priority when a process forks?
Child processes inherit the I/O priority of their parent. If you launch a process with ionice -c 1 ./parent, any child processes spawned by ./parent will also have Real Time I/O priority. To override this, the child must explicitly set its own priority or be moved to a different cgroup.
How do I check the current I/O scheduler?
Run cat /sys/block/<device>/queue/scheduler. The active scheduler is shown in brackets: [mq-deadline] none. Common schedulers include mq-deadline (multi-queue deadline), bfq (Budget Fair Queuing), kyber (low-latency), and none (no scheduler, device handles ordering).
Should I use ionice or cgroup v2 for container environments?
Use cgroup v2 io.weight for container environments. Docker and Kubernetes natively support cgroup resource controls, making it easier to set I/O limits and weights per container. ionice requires manual configuration inside each container or wrapping container entrypoint commands, which is less maintainable at scale.
Does I/O priority affect SSD and NVMe performance?
Yes, but the impact differs from spinning disks. On SSDs and NVMe drives, I/O priority affects queue ordering rather than mechanical seek optimization. Higher priority requests are processed first in the device queue, reducing latency for critical operations. However, NVMe devices with many parallel queues may show less dramatic priority differentiation than traditional SATA SSDs.