Disk I/O scheduling determines the order in which read and write requests are serviced. Choosing the right scheduler for your workload can significantly improve performance, especially under heavy I/O load.
Available Schedulers
# Check available schedulers for a disk
cat /sys/block/vda/queue/scheduler
# [none] mq-deadline kyber bfq
# The current scheduler is shown in brackets
none (No-op)
Passes I/O requests directly to the disk with no reordering. Best for NVMe SSDs and virtualized environments where the hypervisor handles scheduling.
mq-deadline
Ensures every I/O request is serviced within a deadline. Prevents starvation where large sequential writes starve small random reads.
bfq (Budget Fair Queueing)
Provides fair I/O bandwidth distribution among processes. Excellent for multi-tenant servers where fair sharing matters.
kyber
A lightweight scheduler designed for fast NVMe devices. Uses token-based throttling to maintain low latency.
Choosing the Right Scheduler
# Recommendations by storage type:
# NVMe SSD: none or kyber (minimal overhead)
# SATA SSD: mq-deadline or kyber
# HDD: mq-deadline or bfq
# Virtual disk: none (host handles scheduling)
Changing the Scheduler
# Change temporarily
echo "mq-deadline" | sudo tee /sys/block/vda/queue/scheduler
# Change permanently with udev rule
cat << EOF | sudo tee /etc/udev/rules.d/60-scheduler.rules
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
EOF
sudo udevadm control --reload-rules
Benchmarking I/O Performance
# Install fio (Flexible I/O Tester)
sudo apt install fio
# Random read test (simulates database workload)
fio --name=randread --ioengine=libaio --direct=1
--bs=4k --numjobs=4 --size=1G --runtime=30
--rw=randread --group_reporting
# Random write test
fio --name=randwrite --ioengine=libaio --direct=1
--bs=4k --numjobs=4 --size=1G --runtime=30
--rw=randwrite --group_reporting
# Mixed read/write (70/30 split, typical web server)
fio --name=mixed --ioengine=libaio --direct=1
--bs=4k --numjobs=4 --size=1G --runtime=30
--rw=randrw --rwmixread=70 --group_reporting
Monitoring I/O Performance
# Real-time I/O statistics
iostat -x 2
# Key columns:
# r/s, w/s: Reads/writes per second
# await: Average latency per request (ms)
# %util: How busy the disk is
# Per-process I/O
sudo iotop -o
For most Kazepute Breezes running on NVMe storage with KVM virtualization, the none scheduler provides the best performance since the hypervisor handles I/O scheduling at the host level.