Benchmarking VPS NVMe IOPS the Right Way: fio Settings, Block Sizes, and Interpreting Results

Two servers can both say “NVMe SSD” and deliver wildly different database performance. The difference is IOPS under concurrency, and it is invisible unless you measure it correctly. Most people run a single sequential read, get a big number, and conclude the disk is fast. This guide shows the fio settings that actually reveal a plan’s storage behaviour.

Why Single-Stream Tests Mislead

A sequential read measures bandwidth, not IOPS. Databases, mail servers, and container hosts care about random 4K operations at depth. A budget NVMe pool with a hard IOPS ceiling can still push 1 GB/s sequentially while collapsing to 300 IOPS at random, which is what makes a MySQL query take 400 ms instead of 4 ms. If you only ever ran dd, you never saw the ceiling.

The Four Tests Worth Running

  1. Random read, 4K, queue depth 32 — the single most representative number for database work.
  2. Random write, 4K, queue depth 32 — reveals write amplification and thin-provisioning penalties.
  3. Mixed 70/30 read/write, 4K — closer to a real web application workload.
  4. Sequential read, 1M — useful only as a bandwidth ceiling check.

The Commands

Install fio (apt install fio) and run each test against a real file, not a RAM-backed path. Use a 4 GB file so caching cannot flatter the result, and give each test 60 seconds. Run them in the order listed so a write test does not warm the page cache for the read test.

  • fio --name=randread --rw=randread --bs=4k --iodepth=32 --size=4G --runtime=60 --time_based --filename=/root/ioftest --group_reporting
  • fio --name=randwrite --rw=randwrite --bs=4k --iodepth=32 --size=4G --runtime=60 --time_based --filename=/root/ioftest --group_reporting
  • fio --name=mixed --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --size=4G --runtime=60 --time_based --filename=/root/ioftest --group_reporting
  • fio --name=seqread --rw=read --bs=1M --iodepth=8 --size=4G --runtime=60 --time_based --filename=/root/ioftest --group_reporting

Reading the Output

MetricWhere to find itWhat good looks like
IOPSiops= in the report4K random read >2,000 on budget plans; >10,000 on real NVMe
clat (completion latency)clat percentilesp99 under 5 ms is healthy; over 20 ms causes visible app lag
latency distributionlat (msec) blockMost operations in the first two buckets
Bandwidthbw=Sequential matters only for large file transfer

The number to watch is p99 completion latency, not the headline IOPS. A disk averaging 4,000 IOPS with a p99 of 60 ms will produce occasional 500 ms page loads. One averaging 1,500 IOPS with a p99 of 3 ms feels instant. Averages hide the tail that users actually notice.

Interpreting What You See

PatternLikely causeAction
IOPS flat regardless of queue depthProvider IOPS cap per volumeMove data to a plan with higher cap, or reduce random IO
Random write far below readThin provisioning / shared write cacheEnable write-back caching at the app layer, batch writes
p99 latency spikes every ~30 sNoisy neighbour or host-level backup jobRe-test at different times; escalate with evidence
Excellent now, poor after 3 monthsData moved to a slower host poolRe-run quarterly; keep historical CSVs

Reducing IO Pressure Without Changing Plans

  • Increase MySQL innodb_buffer_pool_size so more reads hit RAM instead of disk.
  • Batch small writes — a single 100-row insert beats 100 single-row inserts.
  • Move logs and temp tables to a separate volume if your provider offers one.
  • Turn on the OS write cache (vm.dirty_ratio tuned, not left at default) for bursty workloads.
  • Enable noatime on the data mount — it removes a metadata write on every file read.

Store each run’s CSV in a version-controlled folder. Storage performance on shared infrastructure drifts, and having three months of history turns a vague complaint into a support ticket with evidence. Most providers respond very differently when you can show a before-and-after table.

If the numbers show a genuine storage ceiling, the answer is not always a bigger plan — sometimes it is separating workloads so only the database sits on fast storage. You can compare budget VPS plans on our comparison table and check per-plan storage specs, then pair the results with our guide to server storage cost per gigabyte to avoid paying for capacity you do not use.

It also pays to understand where your storage stops being the bottleneck. Our breakdown of where budget VPS plans break first covers how IOPS, RAM, and CPU interact, so you can tell whether a slow query is really a disk problem or a memory problem masquerading as one.

Re-run the full sequence after any migration or “hardware upgrade” email. It is the fastest way to confirm that a promised improvement actually reached your instance — and the fastest way to catch the opposite.

Benchmarking Across Time, Not Just Once

A single benchmark is a snapshot; a series of them is a diagnostic tool. Run the same four fio tests once a month and keep the CSVs in one folder. After three months you have a baseline, and any deviation becomes visible immediately. A sudden 40% drop in random-read IOPS with no change on your side usually means the provider moved your volume or added tenants to the host — both worth a support ticket, and both almost impossible to prove without history.

Time your tests to the same hour each run. Storage performance on shared infrastructure follows a daily cycle: evening hours in the datacenter’s primary timezone are usually the busiest, and a test run at 3 a.m. will flatter a plan that struggles at 8 p.m. If your application serves users during peak hours, benchmark during peak hours. A number that only holds at 3 a.m. is not the number your users experience.

Finally, document the CPU model alongside each storage result. Providers occasionally migrate instances to newer hosts, and a storage regression can be offset by a CPU gain — or vice versa. Without recording both, you cannot tell which change you are actually observing.

Affordable-Vps-Server-Author
Affordable-Vps-Server-Author
Articles: 342

Leave a Reply