ionice: Controlling Disk I/O Priority

ionice: Controlling Disk I/O Priority

What Is ionice?

Conclusion: ionice sets and queries the disk I/O priority of a process. Where nice controls CPU time, ionice controls who gets to access the disk first.

ionice ships with the util-linux package and is preinstalled on virtually every distribution. You use it to run I/O-heavy jobs (backups, bulk copies) so they stay out of the way of everything else.

There are two basic forms:

# Apply a priority to a command you are about to start
ionice [options] command [args...]

# Change or query a process that is already running (by PID)
ionice [options] -p PID...

Why Do You Need ionice?

Conclusion: When a backup or rsync makes the whole server crawl, the usual cause is disk I/O contention. Lowering the heavy job's priority with ionice protects the response time of your real services.

Even with plenty of free CPU, a single disk serializes I/O requests. When tar, dd, rsync, or du saturates the disk, any web server or database on the same disk suddenly slows to a crawl.

Lowering CPU priority with nice does nothing for delays caused by I/O wait. That is exactly where ionice helps: drop a heavy batch job into the idle class and it only runs "when nobody else is using the disk."

A high wa (iowait) in top means the bottleneck is I/O, not CPU. That is precisely when ionice earns its keep.

How Do You Specify the Class and Priority?

Conclusion: Set the class with -c: 1=realtime (top priority, root only), 2=best-effort (default), 3=idle (only when idle). Use -n for a level from 0 (highest) to 7 (lowest).

There are four I/O scheduling classes.

Number Class Meaning -n priority
0 none No explicit class; derived from the CPU nice value ignored
1 realtime Always gets disk access first; can starve others 0–7
2 best-effort Default class for normal work 0–7
3 idle Runs only when no one else needs the disk ignored

The -n level runs 0 (highest) to 7 (lowest). It only matters for the realtime and best-effort classes; the idle class ignores it (idle is always lowest).

# Run a backup in the idle class (never disturbs other work)
ionice -c 3 tar czf /backup/data.tar.gz /var/data

# Bulk copy at best-effort lowest priority
ionice -c 2 -n 7 cp -a /src/huge.img /mnt/

# realtime class (top priority); requires root
sudo ionice -c 1 -n 0 dd if=/dev/sdb of=/dev/sdc bs=1M

realtime (class 1) requires root and, misused, can completely I/O-starve other processes. In production, stick to idle or best-effort; reach for realtime only with a clear reason.

How Do You Apply It to a Running Process?

Conclusion: Use -p PID on a live process. -p alone shows the current priority; combine it with -c/-n to change it.

You can re-prioritize a heavy job even if you forgot ionice at launch.

# Show the current I/O priority of PID 1234
ionice -p 1234
best-effort: prio 4
# Move the running PID 1234 into the idle class
ionice -c 3 -p 1234

# You can also target by user or process group
ionice -c 3 -u 1000      # all processes of UID 1000
ionice -c 3 -P 5678      # process group 5678

Spot a runaway du or rsync? Grab its PID with pgrep rsync and drop it to idle with ionice -c 3 -p $(pgrep rsync) — you lighten the server without killing the job.

What If ionice Has No Effect?

Conclusion: Only the BFQ scheduler (and the legacy CFQ) honors I/O priorities. The mq-deadline / none schedulers that are default on many modern systems ignore ionice classes.

This is the biggest gotcha. A priority only takes effect once the I/O scheduler interprets it. Historically that was CFQ; today only BFQ does. CFQ was removed in Linux 5.0, and the none or mq-deadline defaults common on NVMe do not distinguish I/O priorities.

First, check the current scheduler for the target disk.

# Check sda's scheduler (the value in [] is active)
cat /sys/block/sda/queue/scheduler
none mq-deadline kyber [bfq]

If [bfq] is selected, ionice works. If it shows [mq-deadline] or similar, switch it.

# Load the bfq module if needed
sudo modprobe bfq

# Switch sda's scheduler to bfq (temporary; reverts on reboot)
echo bfq | sudo tee /sys/block/sda/queue/scheduler

Changing the scheduler affects the disk's overall behavior. BFQ is strong on fairness but can reduce throughput on very high-IOPS NVMe. Validate the impact before making it permanent (e.g. via a udev rule). If -c idle still lets a heavy job crowd everyone out, suspect the scheduler first.

When Do You Use nice vs ionice?

Conclusion: Use nice for CPU-bound work and ionice for I/O-bound work. For jobs that are heavy on both, combine them.

Bottleneck Command Examples
CPU (compute) nice gzip, ffmpeg, builds
Disk I/O ionice tar, rsync, dd, du
Both combine backups in general

Backups consume both CPU (compression) and I/O (read/write), so lowering both is the standard move.

# Lowest priority for both CPU and I/O
nice -n 19 ionice -c 3 tar czf /backup/data.tar.gz /var/data

What Are the Common Real-World Patterns?

Conclusion: The three staples are "nightly backups," "copying/deleting huge files," and "first aid for a runaway process." Keep copy-paste templates built around the idle class.

# 1) Nightly cron backup at idle + nice (no impact on production)
nice -n 19 ionice -c 3 rsync -a /var/www/ /backup/www/

# 2) Delete huge files without hogging the disk
ionice -c 3 rm -rf /var/log/old-huge-dir/

# 3) First aid: drop a running heavy process to idle
ionice -c 3 -p "$(pgrep -d, -f backup-script)"

The -t (--ignore) option lets the command keep running even if setting the priority fails. Handy when you do not want a script to abort on systems whose scheduler does not support priorities.

Next Reading