You ordered an NVMe VPS because someone told you it's five times faster than SATA SSD. Then you migrated your application and saw almost no difference. That's the story I hear in half the tickets about "slow NVMe servers."
The hardware is fast. The configuration is usually wrong. NVMe drives use a completely different I/O path from SATA, and the old tuning defaults actively hurt performance. Most control panels and OS templates ship with settings optimized for spinning rust from 2015. Below are the nine mistakes that cost you most of that speed, plus the correct approach for each.
Mistake 1: Using the wrong I/O scheduler
Linux I/O schedulers were designed to minimize seek time on spinning disks. NVMe has no seek time. The default scheduler on many distributions is still mq-deadline or even cfq on older kernels, both of which add latency NVMe doesn't need.
Check your current scheduler:
cat /sys/block/nvme0n1/queue/scheduler
If you see anything other than none or kyber, you're paying a penalty. For NVMe, none is usually best because the hardware queue depth is so high that software scheduling just adds overhead. On very high-concurrency workloads, kyber can help, but test it.
Set it permanently by adding a udev rule in /etc/udev/rules.d/60-ioscheduler.rules:
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"
Reload with udevadm control --reload && udevadm trigger, then verify. I've seen this single change cut database query latency by 30% on a busy WordPress multisite.
Mistake 2: Leaving the default queue depth
NVMe supports massive queue depths—often 64K commands per queue. SATA maxes out at 32. If your system is still configured for SATA queue depths, you're starving the NVMe controller.
Check current settings:
cat /sys/block/nvme0n1/queue/nr_requests
The default is often 128 or 256. For NVMe under heavy load, bump it:
echo 1024 > /sys/block/nvme0n1/queue/nr_requests
Make it permanent in /etc/rc.local or a systemd service. On a VPS running MySQL with hundreds of connections, this prevents I/O stalls during checkpoint flushes.
Mistake 3: Wrong filesystem or mount options
Ext4 is fine for NVMe, but the default mount options are not. Most templates mount with relatime and no discard, which means you lose TRIM support and waste I/O updating access times.
Here's a better /etc/fstab line for an NVMe root partition:
/dev/nvme0n1p1 / ext4 defaults,noatime,discard 0 1
The noatime flag stops writing every time a file is read. Discard enables continuous TRIM so the SSD's garbage collection stays efficient. XFS is another strong choice for NVMe; it handles parallel I/O better than ext4 on multi-queue devices.
If you're running a database, consider nobarrier only if you have battery-backed cache or can tolerate potential corruption on power loss. Most VPS environments don't guarantee that, so skip it unless you know your provider's setup.
Mistake 4: Ignoring CPU governor settings
NVMe I/O is so fast that the CPU becomes the bottleneck. If your governor is set to powersave, the CPU will ramp up slowly and add milliseconds to every I/O operation.
Check the current governor:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
For a VPS, performance is usually the right choice:
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
Install cpufrequtils and set it permanently:
apt-get install cpufrequtils
echo 'GOVERNOR="performance"' > /etc/default/cpufrequtils
systemctl restart cpufrequtils
This matters more than you'd think. On a cPanel server I worked on, switching from ondemand to performance dropped TTFB by 40ms under load.
Mistake 5: Not tuning virtual memory for NVMe
The kernel's vm.dirty_ratio and vm.dirty_background_ratio settings control when dirty pages get flushed to disk. The defaults (20% and 10%) were designed for slow disks. NVMe can flush much more aggressively without hurting interactivity.
Add these to /etc/sysctl.conf:
vm.dirty_ratio = 10
vm.dirty_background_ratio = 3
vm.dirty_expire_centisecs = 1000
vm.dirty_writeback_centisecs = 100
Apply with sysctl -p. This keeps the page cache cleaner and prevents massive write bursts that can stall other I/O. On database servers, smaller dirty ratios mean more consistent write performance.
So what if your application still feels slow?
Check whether you're actually hitting disk. Run iostat -x 1 during a slow period and look at the %util column for your NVMe device. If it's under 50%, your bottleneck is elsewhere—likely CPU, network, or application code.
If %util is high but throughput is low, you might have tiny random I/O. NVMe excels at this, but only if you're actually sending enough parallel requests. Single-threaded applications won't see much benefit.
Mistake 6: Using swap on NVMe without tuning swappiness
Swap on NVMe is fast enough to be useful, but the default vm.swappiness of 60 is way too aggressive. The kernel will start swapping out pages even when you have RAM free, and once applications hit swap latency goes from microseconds to milliseconds.
Set it lower:
echo "vm.swappiness = 10" >> /etc/sysctl.conf
sysctl -p
A value of 10 tells the kernel to prefer reclaiming cache over swapping out application memory. For most VPS workloads, this is right. If you're running something that genuinely needs more memory than you have, add RAM instead of relying on swap.
Mistake 7: Ignoring NUMA on multi-socket or high-core-count VPS
Some VPS providers offer large instances with multiple NUMA nodes. If your NVMe controller is attached to one node but your application is running on another, you're crossing the interconnect for every I/O.
Check NUMA layout:
numactl --hardware
If you see multiple nodes, pin your performance-critical processes to the node that owns the NVMe device. For a database:
numactl --cpunodebind=0 --membind=0 mysqld
This is rare on small VPS instances, but on 16+ core setups it matters.
Mistake 8: Not monitoring NVMe health and throttling
NVMe drives have thermal throttling. If your VPS provider is cramming too many instances on a single physical NVMe or cooling is inadequate, the drive will slow itself down to prevent damage.
Install nvme-cli:
apt-get install nvme-cli
Check the SMART log:
nvme smart-log /dev/nvme0n1
Look at the temperature and throttle time. If you see non-zero throttle time, your drive is overheating. There's not much you can do on a VPS except complain to your provider or move to better hardware. I've seen this on oversold nodes where twenty VPS instances share one NVMe drive.
Mistake 9: Disabling NCQ or write cache in virtualization settings
Some hypervisors default to cache=none or cache=writethrough for VirtIO disks, which disables write caching and forces synchronous I/O. This kills NVMe performance because every write becomes a blocking operation.
You can't always control this on a VPS, but if you have access to the hypervisor config (KVM/Proxmox), use cache=writeback with discard='unmap' and io='native':
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writeback' io='native' discard='unmap'/>
<source dev='/dev/nvme0n1'/>
<target dev='vda' bus='virtio'/>
</disk>
If you're on a managed VPS, ask your provider what cache mode they're using. cache=none is common for data safety, but it negates most of the NVMe advantage.
Measuring the difference
Before and after making these changes, benchmark with fio. Here's a quick 4K random read test:
fio --name=randread --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --direct=1 --size=1G --numjobs=4 --runtime=60 --group_reporting
You should see IOPS jump significantly after fixing the scheduler and queue depth. On a properly configured NVMe VPS, expect 50K+ IOPS for random 4K reads. If you're seeing under 10K, revisit the checklist.
FAQ
Do I need to reboot after changing the I/O scheduler?
No. The udev rule takes effect immediately for new devices, and you can change it live with echo none > /sys/block/nvme0n1/queue/scheduler.
Will disabling atime break anything?
Rarely. Some backup tools and mail servers check access times, but most modern software doesn't rely on it. Relatime is a safer middle ground if you're unsure.
Can I use these settings on a SATA SSD VPS?
Some of them, yes. The I/O scheduler and mount options help. But SATA SSDs benefit from mq-deadline, not none, because they have lower queue depths.
My provider says the NVMe is "shared storage." Does that matter?
Yes. Shared NVMe often means network-attached NVMe over NVMe-oF or similar, which adds latency. You'll still benefit from these tweaks, but don't expect the same raw IOPS as local NVMe.
Start with the scheduler and mount options
If you fix nothing else, change the I/O scheduler to none and add noatime,discard to your mount options. Those two changes alone recover most of the performance you're losing. The rest—queue depth, CPU governor, VM tuning—compounds the gains.
Check your current settings against this list. Run a quick fio benchmark before and after. On a well-configured NVMe VPS, the difference is obvious.
