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.
| ext4 | XFS | btrfs | |
|---|---|---|---|
| Best for | General web hosting, small files | Large files, high throughput | Snapshots, checksums, compression |
| Max file size | 16 TiB | 8 EiB | 16 EiB |
| Checksums | No | Yes (metadata) | Yes (data + metadata) |
| Online resize | Grow only | Grow/shrink | Grow/shrink |
| Small-disk overhead | Low | Low | Higher (metadata) |
| Verdict | Default choice | Databases, media | Only 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
relatimeif you genuinely need access times.) - discard — passes TRIM to the SSD so freed blocks are reclaimed. On some providers a periodic
fstrim -avcron job is safer than continuous discard; test withfstrim -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 read | Good | OK | Poor |
|---|---|---|---|
| IOPS | 40,000+ | 10,000–40,000 | <5,000 |
| Avg latency | <1 ms | 1–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
You must be logged in to post a comment.