chrt: Make the Kernel's Scheduler Do What You Actually Want

2026-05-19

Everyone reaches for nice when a background job hammers the CPU. nice is a hint. The scheduler weighs it against runtime, sleep history, and a dozen heuristics, then does roughly whatever it wants. If you need the kernel to actually care, you need chrt.

chrt (from util-linux, already on every Linux box) sets a process's scheduling policy, not just its weighting inside one policy. The policies are the levers the kernel actually pulls.

The two policies you'll use most

SCHED_IDLE — runs only when nothing else wants the CPU. Stronger than nice 19: an idle-class task can sit on a busy core forever and never preempt a regular task. Perfect for rsync backups, video encodes, or anything that should disappear until the box is bored.

chrt -i 0 ffmpeg -i source.mkv -c:v libx264 output.mkv

SCHED_FIFO / SCHED_RR — soft real-time. The process preempts every normal task on its CPU until it blocks. Audio engines (jackd, pipewire), trading loops, and ROS nodes live here. Priority 1–99; higher beats lower.

sudo chrt -f 50 jackd -dalsa

Inspecting and changing live processes

Find out what a running process actually is:

$ chrt -p 12345
pid 12345's current scheduling policy: SCHED_OTHER
pid 12345's current scheduling priority: 0

Move a runaway rebuild to the idle class without killing it:

sudo chrt -i -p 0 $(pgrep -f "cargo build")

That's the wizard move — your build keeps going, your tmux pane stays responsive, your video call doesn't stutter, and you didn't have to kill -STOP anything.

The real trick: combine with taskset and ionice

Scheduling policy controls CPU contention. taskset controls which CPUs the task can touch. ionice controls disk contention. They're orthogonal, and together they carve out actual cores and bandwidth.

# Pin to CPU 7, idle scheduling, idle I/O class
taskset -c 7 chrt -i 0 ionice -c3 restic backup /home

That backup will not perceptibly slow anything you're doing. It will also finish, eventually, because SCHED_IDLE still runs — it just yields to anything else that wants the cycles.

Why this beats nice

nice -n 19 still gets meaningful CPU time when the system is loaded, because CFS gives every runnable task some share. SCHED_IDLE gets nothing while other tasks are runnable. The difference shows up the moment you have a CPU-bound foreground task — nice 19 stutters, chrt -i 0 doesn't.

For real-time the comparison is even more lopsided: no nice level will preempt a regular task on a busy core. Only a real-time policy can do that, and chrt is how you opt in without writing C and calling sched_setscheduler(2) yourself.

Gotchas

Key Takeaway: nice asks the scheduler politely; chrt picks the policy class the scheduler actually enforces, and that's the only knob that matters once the system is loaded.

All newsletters