The Linux I/O scheduler decides the order in which block-device requests are processed, and on a VPS it is one of the most misunderstood performance levers in the kernel. Database workloads are especially sensitive to it: the difference between the wrong scheduler and the right one can show up as 20–40% swings in random-read latency, which translates directly into slower queries and higher p99 response times. This article explains how to pick, apply, and verify the I/O scheduler for database workloads on a VPS.
First, Know What You Are Running On
The modern kernel ships with four schedulers: mq-deadline (default on many distros), bfq, kyber, and none (a no-op pass-through). Critically, on NVMe and most cloud block storage the hardware already performs its own reordering and queueing, so a software scheduler can add overhead without adding benefit. Check what is active with:
cat /sys/block/vda/queue/scheduler
lsblk -d -o NAME,ROTA
ROTA=0 means the device is non-rotational (SSD/NVMe); ROTA=1 means a spinning disk. That single value determines your starting point. On cloud VPS instances backed by network storage, the physical device is often a remote SAN or NVMe array, which changes the calculus again.
Which Scheduler for Which Workload
| Scheduler | Best for | Why |
|---|---|---|
| none | NVMe, cloud block storage, single-queue devices | Zero overhead; hardware already optimizes ordering |
| mq-deadline | SSD with mixed read/write workloads | Guarantees read and write deadlines, limits starvation |
| bfq | HDD, shared disks, desktop-class latency fairness | Fairness-oriented; strong for many concurrent processes |
| kyber | Low-latency latency-sensitive apps on fast storage | Targets read/write latency directly, minimal tuning |
For a typical PostgreSQL or MySQL workload on an SSD-backed VPS, the practical choice is none or mq-deadline. On NVMe, none is almost always right. On a shared or spinning-disk VPS (increasingly rare), bfq prevents one noisy process from monopolizing the disk.
Applying the Scheduler at Runtime
You can switch schedulers live, which makes A/B testing easy:
echo none > /sys/block/vda/queue/scheduler
To make it permanent, add a udev rule so it survives reboots:
# /etc/udev/rules.d/60-iosched.rules
ACTION=="add|change", KERNEL=="vd*", ATTR{queue/scheduler}="none"
If you use a kernel command-line parameter instead (elevator=none), remember that it only applies to the boot device and is ignored for devices probed later. The udev approach covers all block devices consistently.
Verify with Real Benchmarks, Not Opinions
Scheduler changes are workload-dependent, so benchmark before and after. fio with a database-shaped profile is the right tool:
fio --name=db-sim --rw=randrw --rwmixread=70 --bs=8k \ --iodepth=16 --numjobs=4 --size=2G --runtime=60 \ --time_based --group_reporting70% reads at 8 KB blocks approximates OLTP traffic. Record IOPS and latency percentiles under each scheduler. A meaningful difference is one you can reproduce across multiple runs — a 5% IOPS swing is noise; a 30% p99 latency change is real. For an even more realistic test, run your actual database's workload replay (
pgbenchfor PostgreSQL,sysbench oltpfor MySQL) and watch query latency directly.Interaction with Other I/O Settings
The scheduler is only one layer of the I/O stack. On a VPS, three adjacent knobs change the outcome:
- Queue depth (nr_requests) — for databases, a deeper queue helps absorb bursty OLTP traffic; on NVMe the device handles depth internally.
- Filesystem mount options —
noatimeeliminates metadata writes on every read; for databases considernobarrieronly on battery-backed storage, never on a VPS without guarantees. - VM dirty ratios —
vm.dirty_ratioandvm.dirty_background_ratiocontrol writeback bursts. If you see periodic latency spikes, tune these before blaming the scheduler.
Remember that on most cloud VPSes the "disk" is remote network storage. The host-side scheduler on your instance is often less important than the storage vendor's own queueing, which is exactly why none wins so often in benchmarks — there is nothing left for the guest kernel to optimize.
When Scheduler Tuning Is Not the Answer
If your database is slow and the disk is an SSD-backed cloud volume, the scheduler is rarely the bottleneck. Check the more common culprits first: insufficient innodb_buffer_pool_size (MySQL) or shared_buffers (PostgreSQL), missing connection pooling, missing indexes, and CPU steal from an oversold host. If iostat -x shows low utilization and %util near 100%, you may be looking at queue saturation on the remote storage side — a provider-level constraint no scheduler fixes. In that case, compare VPS providers on our comparison table, because storage quality varies far more between providers than between schedulers.
Also keep the rest of the memory subsystem consistent with your I/O changes. Swap behavior, the page cache, and the I/O scheduler all interact under pressure — tuning one in isolation while the others fight it is a common failure mode. A good monitoring setup (iostat, iotop, and a metrics dashboard) turns scheduler tuning from guesswork into a measured decision. If you are evaluating hosts for a database-heavy VPS, see the full storage specs on our VPS comparison page, and InterServer's NVMe VPS plans are a reasonable baseline for this kind of workload.
Summary
For database workloads on modern VPS hardware, start with none on NVMe and mq-deadline on SSD, then verify with fio and a real database workload before committing. Apply changes through udev so they survive reboots, benchmark with percentile-aware tools, and keep the scheduler in context — storage vendor queueing and database memory settings usually matter more. Measure, change one variable, measure again. That discipline is worth more than any scheduler default.



Leave a Reply
You must be logged in to post a comment.