Every plan we sell, from the $3.29 VPS to the largest dedicated server, runs on NVMe. That is a line on the pricing page; this post is the evidence behind it. We ran the same fio profiles on our production hosts, on a SATA SSD array from a previous generation, and on a customer-visible VPS slice, and we explain which of the numbers your database actually feels.
Method
All tests use fio 3.36, direct I/O, a 16 GB test file to defeat caches, 60-second runs after a 30-second warm-up, three runs averaged. The three systems:
- NVMe host: two enterprise NVMe drives (PCIe 4.0, 3 DWPD) in a software mirror, the configuration under our VPS hosts and the default on dedicated servers.
- SATA host: four enterprise SATA SSDs in RAID 10 behind a hardware controller, the configuration we retired in 2023.
- VPS-4: a KVM guest on an NVMe host with the standard IOPS budget, measured from inside the guest with virtio-blk.
The numbers
| Profile | NVMe host | SATA host | VPS-4 guest |
|---|---|---|---|
| Random 4k read, QD1, latency p50 | 68 µs | 142 µs | 95 µs |
| Random 4k read, QD1, latency p99 | 110 µs | 390 µs | 210 µs |
| Random 4k read, QD32, IOPS | 612,000 | 178,000 | 40,000 (budget) |
| Random 4k write, QD1, latency p99 (fsync) | 28 µs | 620 µs | 70 µs |
| Random 4k write, QD32, IOPS | 380,000 | 72,000 | 20,000 (budget) |
| Sequential read, 1 MB, MB/s | 6,900 | 2,050 | 1,800 (budget) |
| Sequential write, 1 MB, MB/s | 4,200 | 1,600 | 900 (budget) |
| Mixed 70/30 4k, QD16, IOPS | 420,000 | 105,000 | 32,000 (budget) |
The VPS column is capped by policy, not by hardware: each guest gets a budget so that no neighbour can consume the host. The budget is the point of the comparison. A VPS-4 at its ceiling still beats the whole SATA array on latency, and latency is the number that matters.
Why latency, not throughput
A web request that touches a database performs a chain of small dependent reads: an index page, then a heap page, then another index for a join. Each is a 4k or 8k read that must finish before the next can start, so the request's time is the sum of the latencies, not a function of bandwidth. A query that touches 200 pages costs 14 ms on NVMe and 28 ms on SATA at the median, and the difference at the 99th percentile is what users notice as “sometimes slow”.
Writes are worse. Every committed transaction ends in an fsync of the write-ahead log. On the SATA array that is 620 µs at p99; on NVMe it is 28 µs. A busy PostgreSQL doing 2,000 commits per second spends 1.2 seconds of every second waiting on SATA fsyncs and 56 ms on NVMe. That is the entire difference between “we need a bigger server” and “it is fine”.
What each database feels
- PostgreSQL: commit latency and random reads of index pages. NVMe lets you keep
synchronous_commit = onwithout paying for it; on SATA, people turn it off and lose durability to get speed back. - MySQL / MariaDB: the InnoDB redo log has the same fsync profile, and the doublewrite buffer makes SATA hurt twice.
- Redis: mostly RAM, but
appendfsync everysecblocks briefly on slow disks and shows up as periodic latency spikes; on NVMe the spike disappears. - ClickHouse, Elasticsearch: sequential throughput matters for merges and big scans; both are close to the host's sequential ceiling, and this is the workload where a dedicated server's unlimited NVMe pays for itself over a VPS budget.
- SQLite: single-writer with fsync per transaction; on SATA it feels slow, on NVMe it handles thousands of writes per second on a $3 VPS.
Noisy neighbours and budgets
The alternative to per-guest budgets is slower disks for everyone, which is what oversold hosts with “SSD storage” usually ship. Our budget is per plan, published on the pricing page, and enforced by the hypervisor's I/O throttling in a way that returns unused capacity to the pool. In practice a VPS-4 sees its full budget more than 99% of the time; the remainder is short contention during a neighbour's backup, visible as p99 spikes of a few hundred microseconds rather than seconds.
If you need the ceiling removed, a dedicated server is the honest answer. The mirrored NVMe pair delivers the first column of the table to you alone, and the ZFS on root add-on trades about 5% of it for checksums and snapshots, which is a good trade for anything holding data you care about.
Reproduce it
apt install -y fio
fio --name=randread --filename=/srv/fio.test --size=16G --direct=1 --rw=randread \
--bs=4k --iodepth=1 --runtime=60 --time_based --ioengine=io_uring --group_reporting
fio --name=fsync --filename=/srv/fio.test --size=16G --direct=1 --rw=randwrite \
--bs=4k --iodepth=1 --fsync=1 --runtime=60 --time_based --ioengine=io_uring --group_reporting
Look at clat percentiles in the output rather than the headline IOPS. Delete the test file afterwards; 16 GB is a noticeable slice of a VPS-1.
