Andrew Mercer
on this page

ionice sets or shows a process's I/O scheduling class and priority — the disk equivalent of nice. Typical use: keep backups, rsync, find, or log compression from making the rest of the system sluggish. It pairs naturally with nice/renice for CPU, and with rsync backup jobs run from cron, so they don't compete with foreground work.

Syntax

ionice [-c class] [-n level] [-t] -p PID...      # change running processes
ionice [-c class] [-n level] [-t] -P PGID...     # process groups
ionice [-c class] [-n level] [-t] -u UID...      # all of a user's processes
ionice [-c class] [-n level] [-t] COMMAND [ARGS] # start a command
ionice -p PID                                    # show current setting

Classes

-c Class Behaviour
0 none Default. Priority is derived from the CPU nice value: (nice + 20) / 5 within best-effort
1 realtime Always served first. Levels 0–7. Root only — can starve everything else, including the system
2 best-effort Normal class. Levels 0 (highest) – 7 (lowest), default 4
3 idle Only gets disk time when nobody else wants it. No level. No root needed

-n sets the level (0–7) for classes 1 and 2. -t ignores failures to set the class (useful in scripts).

Examples

# Run a backup only when the disk is otherwise idle
ionice -c3 tar czf /backup/home.tar.gz /home

# Lowest best-effort priority, combined with low CPU priority
nice -n 19 ionice -c2 -n7 rsync -a /data/ /mnt/backup/

# Check a process
ionice -p 1234
# best-effort: prio 4

# Demote a running process
sudo ionice -c3 -p 1234

# Demote everything a user is running
sudo ionice -c2 -n7 -u builduser

# Give a critical database real-time I/O (careful)
sudo ionice -c1 -n0 -p $(pgrep -o postgres)

When it actually works

ionice only sets a hint; the block I/O scheduler decides whether to honour it.

cat /sys/block/sda/queue/scheduler
# none mq-deadline kyber [bfq]
Scheduler Honours ionice?
bfq Yes — classes and levels
mq-deadline Partially — separates realtime / best-effort / idle classes; levels within a class are mostly ignored
kyber, none No

NVMe devices default to none, so ionice usually does nothing on them. Switch to bfq if you need it (at some throughput cost):

echo bfq | sudo tee /sys/block/nvme0n1/queue/scheduler

The old cfq scheduler that ionice was designed around was removed in kernel 5.0.

The buffered-write caveat

Most writes go into the page cache and are flushed later by kernel writeback threads — not by your process. The I/O priority mostly applies to reads and direct/synchronous writes. An ionice -c3 process doing heavy buffered writes can still saturate the disk.

For real write throttling, use cgroup v2 limits instead (below).

Better option on systemd systems: cgroups

The cgroup v2 io controller works regardless of scheduler for limits, and accounts for writeback:

# One-off command with lower I/O weight (default 100, range 1–10000)
sudo systemd-run --scope -p IOWeight=10 rsync -a /data/ /mnt/backup/

# Hard bandwidth cap
sudo systemd-run --scope -p "IOWriteBandwidthMax=/dev/sda 20M" dd if=/dev/zero of=/tmp/big bs=1M count=2000

In a unit file:

[Service]
IOSchedulingClass=idle          # ionice equivalent (best-effort, realtime, idle)
IOSchedulingPriority=7          # 0-7
IOWeight=10                     # cgroup weight (BFQ honours it)
IOReadBandwidthMax=/dev/sda 50M

IOWeight relies on BFQ for proportional sharing; the *BandwidthMax / *IOPSMax limits work with any scheduler.

Quick reference

Goal Command
Show priority ionice -p PID
Background job ionice -c3 CMD
Low priority, still progresses ionice -c2 -n7 CMD
Low CPU + I/O nice -n19 ionice -c3 CMD
Check scheduler cat /sys/block/DEV/queue/scheduler
Hard limit instead systemd-run --scope -p "IOWriteBandwidthMax=DEV RATE" CMD

See also

  • man 1 ionice, man 2 ioprio_set
  • nice, renice, chrt, taskset
  • rsync, cron
  • The Linux Kernel — I/O schedulers and cgroups
  • Kernel docs: Documentation/block/ioprio.rst, Documentation/admin-guide/cgroup-v2.rst