To optimize disk I/O on a Linux VPS you change three things: the kernel I/O scheduler for the virtual disk, the mount options your filesystem boots with, and the memory/swap settings that decide when writes hit the disk. This guide walks through measuring your current performance, applying each change safely, and verifying the result — with a rollback path at every step.
Most slow-VPS complaints are not "the disk is slow". They are the kernel writing too eagerly, filesystems recording access times nobody reads, or swap thrashing during a memory spike. Fixing that is a configuration job, not a hardware upgrade.
Before you start
You need root (or a sudo user) on a Linux VPS running Ubuntu 22.04/24.04, Debian 12 or AlmaLinux/Rocky 8–9.
Warning: Every change in this guide touches /etc/fstab, kernel parameters or swap. A bad fstab entry can stop the server from booting. Keep the browser console in your VPS provider's control panel open in another tab, and never reboot until findmnt --verify passes.
Gather the facts first:
- Confirm what disk you actually have. A
virtioor NVMe device behaves differently from emulated SATA. - Note free space. Anything below roughly 10–15% full slows writes on most filesystems.
- Know your memory size — swap tuning depends on it.
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,ROTA
df -hT
findmnt --verify
free -h
swapon --showROTA 0 means the kernel sees a non-rotational (SSD-backed) device. findmnt --verify should print "Success, no errors or warnings detected" before you touch anything.
Measure the current disk I/O on your VPS
Numbers first, opinions later. Install fio and run one direct-write test against a scratch file.
sudo apt update && sudo apt install -y fio # Debian/Ubuntu
sudo dnf install -y fio # AlmaLinux/Rockyfio --name=randwrite --ioengine=libaio --iodepth=16 --rw=randwrite \
--bs=4k --direct=1 --size=512M --numjobs=1 --runtime=60 \
--group_reporting --filename=/var/tmp/fio-testWarning: fio writes real data to /var/tmp/fio-test. Make sure the partition has at least 1 GB free, and delete the file when you are finished.
Record the write: IOPS and clat figures. Run the same command again after your changes so you compare like with like. A quick alternative for latency-sensitive databases is ioping, which shows per-request delay rather than throughput.
Set the I/O scheduler
cat /sys/block/vda/queue/scheduler
cat /sys/block/vda/queue/rotationalFor SSD-backed virtual disks the usual choices are:
none— no reordering, lowest overhead, best on fast NVMe.mq-deadline— adds bounded latency for every request, good for busy multi-tenant hosts.kyber— lightweight queueing for fast devices, available on recent kernels.
Avoid bfq on a VPS unless you specifically need fairness between processes; it costs CPU.
Make it persistent with a udev rule so it survives reboots without editing a bootloader:
# /etc/udev/rules.d/60-io-scheduler.rules
ACTION=="add|change", KERNEL=="vd*", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="sd*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="nvme*", ATTR{queue/scheduler}="none"Apply and confirm:
sudo udevadm control --reload && sudo udevadm trigger
cat /sys/block/vda/queue/schedulerTune mount options in /etc/fstab
Open /etc/fstab and edit only the options field (the fourth column). Keep the UUID exactly as it is.
sudo cp /etc/fstab /etc/fstab.bak
sudo findmnt --verifyTwo commonly useful changes:
noatime— stop recording every file read. On busy web servers this removes a large share of small writes with no downside for normal workloads. Userelatimeinstead if some tool depends on access times.errors=remount-ro(ext4) — mount read-only after a filesystem error instead of carrying on and corrupting more data.
# /etc/fstab (ext4 example)
UUID=0d5a1f5e-1d3e-4e6a-9c1a-2f0b7c9d4e11 / ext4 defaults,noatime,errors=remount-ro 0 1For XFS, noatime,nodiratime and optional logbsize=64k are the usual starting points. For discard, prefer the weekly fstrim timer over continuous TRIM — online discard can make deletes noticeably slower on loaded virtual disks.
sudo systemctl enable --now fstrim.timer
systemctl list-timers fstrim.timerTest the entry without rebooting:
sudo findmnt --verify --verbose
sudo mount -o remount / && findmnt /Warning: If findmnt --verify reports errors, fix /etc/fstab before rebooting. A malformed entry can drop the server into an emergency shell that only your VPS provider's console can reach.
Tune kernel dirty-page and cache settings
The kernel buffers writes in RAM and flushes them later. If the flush thresholds are too high, a memory-heavy process can trigger a long stall while gigabytes are written at once.
# /etc/sysctl.d/99-io-tuning.conf
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 15
vm.vfs_cache_pressure = 50dirty_background_ratio— start background flushing at this percentage of RAM.dirty_ratio— processes block once dirty data reaches this percentage.vfs_cache_pressure— lower values keep dentry/inode cache longer, which helps file-heavy workloads.swappiness— how aggressively anonymous pages move to swap.
Apply without a reboot:
sudo sysctl --system
sysctl vm.swappiness vm.dirty_ratioIf the VPS is small (1–2 GB RAM), consider absolute limits instead of ratios, for example vm.dirty_bytes = 268435456 and vm.dirty_background_bytes = 67108864, so a single large write cannot occupy a quarter of memory.
Tune swap for a VPS
Swap does not make a disk faster, but the wrong swap setup makes I/O look slow. Check what you have:
swapon --show
cat /proc/swapsIf there is no swap at all, add a swap file before tuning anything else — see Add a swap file on a Linux VPS.
For a VPS with enough RAM, vm.swappiness = 10 keeps the kernel from paging idle services out. Do not set it to 0 unless you have a specific reason; that removes the safety valve and can turn a memory spike into an OOM kill.
Tip: On hosts where swap lives on the same virtual disk as your data, heavy swap use competes directly with database I/O. Watch si/so in vmstat 1 — sustained non-zero values mean memory pressure, not a disk problem.
Check it works
findmnt / | grep -o 'noatime'
cat /sys/block/vda/queue/scheduler
sysctl vm.swappiness vm.dirty_ratio
systemctl is-active fstrim.timer
free -h && swapon --showThen re-run the same fio command you used for the baseline and compare IOPS and completion latency. Remove the test file:
rm -f /var/tmp/fio-testFinally, reboot once during a maintenance window and confirm everything came back: mount, sysctl, scheduler and swap settings should all match what you configured.
Troubleshooting
- Server does not boot after editing fstab — a syntax error or wrong UUID. Connect through your VPS provider's console, run
mount -o remount,rw /, restore/etc/fstab.bak, fix the entry and runfindmnt --verifybefore rebooting again. - "cannot remount /dev/vda1 read-write, is write-protected" — ext4 hit an error and remounted read-only because of
errors=remount-ro. Checkdmesg | tail -50for the underlying fault, runfsckon the filesystem from rescue/console, then remount. - Deletes became slower after enabling TRIM — online
discardis the cause. Removediscardfrom/etc/fstaband rely onfstrim.timerinstead. - Swap is configured but never used —
swapon --showprobably lists nothing, or the file has wrong permissions. Recheck withswapon --showand the swap file's600mode. - Write latency spiked after tuning — dirty limits set too low for the workload. Raise
vm.dirty_ratiogradually and re-test withfiorather than changing several values at once.
Next steps
- Monitor CPU, memory, disk and logs on a Linux server to catch I/O pressure early.
- Check your VPS CPU, RAM and disk from the command line before planning further tuning.
- Free up disk space on a Linux VPS so the filesystem is not fighting for free blocks.
