VPS Disk I/O Tuning: Filesystem Choices, Mount Options, and fio Benchmarks

On a VPS, disk I/O is usually the first performance wall you hit. CPU and RAM are easy to reason about, but storage performance depends on the host’s hardware, the hypervisor’s throttling, the filesystem, mount options, and the I/O scheduler — all stacked on top of each other. The good news: you can measure each layer and fix the ones you control. This guide covers filesystem choice, mount options, I/O schedulers, and how to benchmark everything with fio so you tune on data, not vibes.

Before tuning, know what hardware you actually have — compare VPS plans side by side to see which providers put NVMe in your plan tier, because no amount of tuning fixes a spinning disk.

Step 0 — Know your storage type

Three questions determine your ceiling. Is the disk local NVMe, local SSD, network block storage (SAN), or HDD? Is it shared with other tenants? Is the hypervisor throttling IOPS? Find out with:

lsblk -d -o name,rota,size,model
cat /sys/block/vda/queue/rotational

rota=1 means a spinning disk; rota=0 means SSD/NVMe. Network block storage (common on cloud providers) adds network latency on top of the disk, which is why its IOPS numbers are lower but consistent.

Filesystem choice: ext4 vs. XFS vs. btrfs

Modern distributions default to ext4, and for most VPS workloads that is the right answer: mature, fast on small files, and trivially repairable. XFS wins for large files and high sustained throughput (big databases, media, backups). btrfs adds checksums, snapshots, and compression, but its metadata overhead and COW behavior cost performance on small disks, and it needs monitoring. Do not choose btrfs purely for snapshots on a 40 GB VPS — use provider-level snapshots instead.

ext4XFSbtrfs
Best forGeneral web hosting, small filesLarge files, high throughputSnapshots, checksums, compression
Max file size16 TiB8 EiB16 EiB
ChecksumsNoYes (metadata)Yes (data + metadata)
Online resizeGrow onlyGrow/shrinkGrow/shrink
Small-disk overheadLowLowHigher (metadata)
VerdictDefault choiceDatabases, mediaOnly if you need its features

Mount options that matter

Your /etc/fstab line controls write behavior. For SSD/NVMe-backed VPS, the practical set is:

UUID=xxxx / ext4 defaults,noatime,discard 0 1
  • noatime — stops the kernel from writing a read timestamp on every file access. On busy web servers this removes a large fraction of write I/O. (Use relatime if you genuinely need access times.)
  • discard — passes TRIM to the SSD so freed blocks are reclaimed. On some providers a periodic fstrim -av cron job is safer than continuous discard; test with fstrim -v /.
  • commit=30 — batches ext4 journal commits every 30s instead of 5s, cutting journal write frequency at the cost of up to 30s of data loss on a hard crash. Fine for caches, wrong for databases.
  • nofail — essential for optional/network mounts so the system boots even if the volume is missing.

I/O scheduler: none vs. mq-deadline

Modern kernels default to none (noop) for NVMe and mq-deadline for SATA, and the defaults are usually right. none hands requests straight to the device, which is correct for NVMe where the drive does its own queuing. mq-deadline reorders requests to bound latency, which helps spinning disks and some SATA SSDs. Check what is active:

cat /sys/block/vda/queue/scheduler
# set persistently via a udev rule
echo 'ACTION=="add|change", KERNEL=="vd*", ATTR{queue/scheduler}="none"' > /etc/udev/rules.d/60-iosched.rules

Also worth setting: queue/nr_requests higher (512) for NVMe, and for virtualized disks disable the elevator entirely — the hypervisor is already doing the heavy scheduling.

Benchmark with fio

fio is the standard tool. Install it (apt install fio) and run the two benchmarks that matter for web workloads — random 4K reads (database pattern) and sequential writes (log/backup pattern):

fio --name=randread --rw=randread --bs=4k --size=1G \
    --iodepth=32 --ioengine=libaio --direct=1 --numjobs=4

fio --name=seqwrite --rw=write --bs=1M --size=2G \
    --iodepth=16 --ioengine=libaio --direct=1

Use --direct=1 to bypass the page cache, or you will benchmark RAM instead of disk. The reported IOPS and latency (clat) lines are what you read. Quick reality check for a single 4K reader on modern VPS storage:

4K random readGoodOKPoor
IOPS40,000+10,000–40,000<5,000
Avg latency<1 ms1–5 ms>10 ms

Run the benchmark three times at different hours. If IOPS vary wildly between runs, the storage is shared and throttled — that is a provider limitation no mount option fixes, and a strong reason to see the full specs of providers that advertise dedicated NVMe before renewing.

Putting it together

A sane baseline for a Linux VPS: ext4 with noatime, scheduler none on NVMe, a weekly fstrim, and a recorded fio baseline in your runbook so you can spot degradation before users do. Filesystem and scheduler changes give you maybe 5–15% — choosing NVMe-backed storage in the first place gives you 10–50x. Tune the layers you control, but buy the hardware that sets the ceiling.

Leave a Reply