Most Linux VPS images ship with kernel defaults chosen for compatibility across the widest range of hardware and workloads — not for the single virtual machine you are running. Three settings cause the majority of avoidable performance loss on a small instance: the CPU frequency governor (when exposed at all), the swappiness value that pushes anonymous memory to disk too eagerly, and the I/O scheduler assigned to virtual block devices. This guide walks through checking each, changing it safely, and proving whether the change helped.
Establish a Baseline Before Changing Anything
Record the current state and a workload measurement first, or you will have no way to tell a real improvement from noise. Save the sysctl and block-device configuration, then run a short representative load — a build, an HTTP benchmark, or a database query mix — and keep the latency numbers. Cloud hosts vary enough between runs that an unmeasured change is indistinguishable from luck.
sysctl kernel.sched_* vm.swappiness vm.dirty_ratio vm.vfs_cache_pressure
for d in /sys/block/vd* /sys/block/sd* /sys/block/nvme*; do
[ -e "$d/queue/scheduler" ] && echo "$d: $(cat $d/queue/scheduler)"
done
cpufreq-info 2>/dev/null | head -20 || echo "no cpufreq exposed"
sysbench --test=cpu --cpu-max-prime=20000 run | tail -3
Write the captured values to a file outside the VM as well. If a tuning experiment makes things worse, you want the original numbers available without guessing at distribution defaults.
CPU Governors: What You Can and Cannot Control
The governor controls how the cpufreq subsystem chooses a frequency for a core. On bare metal, switching from powersave or schedutil to performance removes ramp-up delay after idle periods. On a KVM guest, the hypervisor usually owns the real frequency of the physical core, and what the guest sees is either a simulation or nothing at all.
Check what exists before planning any work: ls /sys/devices/system/cpu/cpu0/cpufreq/ will either list scaling_governor and scaling_available_governors or be empty. If it is empty, frequency tuning is not available to you and the delivered clock is whatever the host decides. If it is present, read the available governors and whether the files are writable — on many providers they are read-only even when they appear.
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# try it, and read back the result
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
If the write fails, the guest lacks permission and no userspace tool will change that. What you can still verify is whether the delivered frequency is stable: sample scaling_cur_freq or /proc/cpuinfo during a sustained load. A frequency that drops partway through a long run means host-level thermal or quota throttling, a different problem from governor choice.
Making a Governor Change Persist
A tee into sysfs lasts until reboot. For a permanent setting use cpufrequtils or a systemd unit, and verify after a restart rather than assuming the package hook runs on your distribution. If you are using tuned, its profiles manage the governor for you and will overwrite manual changes — pick one mechanism.
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtils
sudo systemctl enable --now cpufrequtils
# or, on systems using tuned
sudo tuned-adm profile throughput-performance
sudo tuned-adm active
Note that performance keeps cores at maximum frequency and raises host power draw; on a shared host that may be irrelevant to your bill but is rarely what the provider wants, and some hypervisors clamp it anyway. Measure the difference on your own workload before keeping it — for I/O-bound services the effect is usually inside the noise floor.
Swappiness: The Default Is Too Aggressive for Small VPS
vm.swappiness tells the kernel how willing it should be to reclaim anonymous pages instead of page cache. The default of 60 is tuned for desktops where swap is plentiful; on a 1-2 GB VPS with a small swap file, it means the kernel will evict hot application memory to disk while still holding file cache. The result is latency spikes under memory pressure that look like disk problems.
For a server that runs a database or a language runtime with a working set close to RAM size, a value of 10 is a reasonable starting point, and 1 is defensible when swap exists only as an emergency buffer. Do not set 0 on kernels where it still means “avoid swap unless absolutely necessary” — the difference between 1 and 0 is small, but 1 keeps an escape hatch.
cat /proc/sys/vm/swappiness
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/60-vps-tuning.conf
sudo sysctl --system
# check that pages are actually staying resident
vmstat 5 12
Watch the si and so columns in vmstat after the change. Persistent swap-in with low cache pressure means the working set genuinely exceeds RAM and swappiness was never the problem — resize the instance or reduce the working set. Also check vm.vfs_cache_pressure: lowering it keeps dentries and inodes cached, which helps on hosts with many small files but delays reclaim of memory you may need.
I/O Schedulers on Virtual Block Devices
A virtual disk is not a physical disk. Requests pass through virtio to the host, which does its own scheduling on real hardware. That is why the common recommendation on modern kernels is none (or mq-deadline where none is unavailable) for SSD-backed VPS volumes: the guest-side scheduler adds merge and sort work without any visibility into the device’s real queue.
The exception is a spinning-disk-backed plan or a heavily contended shared volume where request ordering matters for throughput. There, bfq can improve fairness between competing processes at the cost of latency. The only way to know which applies is to test both under your workload rather than copy a recommendation from a different provider.
cat /sys/block/vda/queue/scheduler # shows [none] mq-deadline kyber bfq
echo mq-deadline | sudo tee /sys/block/vda/queue/scheduler
fio --name=r --filename=/tmp/fiotest --size=512M --bs=4k \
--rw=randread --iodepth=16 --numjobs=4 --runtime=30 --time_based
A scheduler change on a guest volume is frequently smaller than run-to-run noise. Repeat each configuration three times and compare the median IOPS and p99 latency, not the best run. If the difference is inside the variance, keep the default — fewer moving parts survive kernel upgrades and provider migrations.
Verify the Changes Persist Across Reboots
Distributions differ in which sysctl directories they read and in what order, and a provider image may include its own tuning file that overrides yours. After writing configuration, reboot once and re-read every value. If a setting reverted, check for a competing file with a higher sort order in /etc/sysctl.d/ or a cloud-init script that reapplies it on each boot.
grep -r . /etc/sysctl.d/ /etc/sysctl.conf | grep -E 'swappiness|vfs_cache'
sudo reboot
# after reboot
cat /proc/sys/vm/swappiness
cat /sys/block/vda/queue/scheduler
systemctl status cpufrequtils --no-pager
Record the verified post-reboot values alongside the baseline so the next person knows what was intentionally changed and why. Undocumented sysctl tweaks are a common source of “this host behaves differently from the test host” incidents.
When These Tweaks Do Not Apply
If the guest reports no cpufreq entries and only a single none scheduler, the provider has already made these decisions at the host layer and guest-side tuning is limited to memory behaviour. In that case the useful levers are elsewhere: reducing the working set, moving hot data to local storage if the plan offers it, and checking whether the instance is CPU-constrained by steal rather than by configuration.
Steal is worth ruling out early, since no governor or scheduler setting fixes contention on the physical core. Sampling the steal column of /proc/stat over a few minutes is enough to tell whether the ceiling is yours or the host’s; the method is described in this walkthrough on measuring steal time. If the tuning that remains is driven by disk and network rather than by CPU, a larger instance with guaranteed resources is cheaper than working around the limits — the plan differences are summarised in our VPS plan comparison.
Conclusion
CPU governors, swappiness, and I/O schedulers are worth checking once, on every new instance, and then verified after reboot and left alone. The governor is often not yours to control in a guest; swappiness is almost always worth lowering from 60 on a small VPS; and the I/O scheduler choice matters mainly on contended or spinning-disk-backed volumes. Make one change at a time, measure with the same workload, and revert anything that does not produce a repeatable improvement.



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