VPS Performance Testing: How to Benchmark CPU, RAM, Disk, and Network

Most VPS benchmark articles stop at a single sysbench number. That number is nearly useless on its own: it hides noisy-neighbor interference, disk write amplification, and the difference between sequential and random I/O that decides whether your database is fast. A credible performance baseline needs four independent measurements — CPU, memory, disk, and network — taken under controlled conditions and recorded so they can be compared over time.

This walkthrough covers the commands, the parameters that change results, and the thresholds that separate a healthy instance from one you should not keep. Run everything on a fresh instance before production data lands, and store the outputs. The VPS versus dedicated server comparison is useful background for interpreting why shared instance results vary between runs.

Establishing a Controlled Test Environment

Benchmark results from a tuned production box are not comparable to results from a fresh install. Decide which you want: for provider evaluation, use a clean image with no services running. Disable anything that writes to disk or consumes CPU during the test, and record the kernel version with uname -r, the CPU model from lscpu, and the storage driver with lsblk -o NAME,ROTA,SCHED. Note whether ROTA is 0 (SSD/NVMe) or 1.

Warm-up matters. Drop caches between disk tests with sync; echo 3 > /proc/sys/vm/drop_caches so you are measuring the device rather than page cache, unless you are deliberately testing cached read performance. Also check the clock source with cat /sys/devices/system/clocksource/clocksource0/current_clocksource; on virtualized guests, tsc is preferred over hpet, and a wrong clock source inflates timing variance in every subsequent test.

CPU: Throughput Plus Scheduler Latency

Run sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) --time=120 run and record both events per second and the 95th percentile latency. Then repeat with half the thread count. On a healthy shared vCPU, scaling from 2 to 4 threads should approach a 1.8× improvement; if it plateaus at 1.1×, the vCPUs are contending for the same physical thread or the guest is cgroup-throttled. Cross-check with mpstat -P ALL 1 during the run for %steal and %idle.

For a more realistic mixed load, use stress-ng --cpu 4 --io 2 --vm 2 --vm-bytes 1G --timeout 120s --metrics-brief. The bogo-ops figure gives a composite score, but the useful part is watching whether /proc/pressure/cpu shows sustained some avg10 above 5 during the run. If it does while your own utilization is under 70%, the host is oversubscribed and your scheduler is waiting on someone else’s work.

Memory: Bandwidth and Latency, Not Just Size

free -m tells you what was allocated, not how fast it moves. Measure actual bandwidth with sudo apt install mbw then mbw -n 20 256, which performs a memory copy and reports MiB/s. A modern DDR4 virtualized guest should land in the 8–20 GiB/s range depending on the plan’s memory channel allocation; anything under 5 GiB/s on a supposedly performance-tier plan suggests bandwidth contention. Complement that with access latency using sysbench memory --memory-block-size=1K --memory-total-size=10G --memory-access-mode=rnd run.

Also verify huge pages, which materially affect database and JVM performance. Check availability with grep -i huge /proc/meminfo. Transparent huge pages set to madvise is a sane default; always can cause latency spikes under allocation churn, and never forces manual reservation. If you plan to run a database, confirm HugePages_Total can be reserved at the size you need — some providers cap it unexpectedly on smaller plans.

Disk: Random IOPS, Sequential Throughput, and Latency

Random 4K performance is the number that predicts database behavior. Use fio --name=randread --ioengine=libaio --direct=1 --bs=4k --iodepth=32 --numjobs=4 --rw=randread --filename=/tmp/testfile --size=2G --runtime=60 --group_reporting. Record IOPS and the clat percentile block. For NVMe-backed plans, expect random read IOPS in the tens of thousands and p99 latency under 1 ms; SATA SSD-backed plans typically land near 5–15k IOPS with p99 around 1–3 ms.

Then test sequential writes with --rw=write --bs=1M --iodepth=16. A plan advertised as NVMe that sustains only 150 MB/s sequential write is usually throttled by an IOPS or bandwidth quota, not by the device. Watch iostat -x 1 during the run: aqu-sz above 8 with w_await climbing linearly indicates queueing at the device or quota layer. Also run fio --rw=randrw --rwmixread=70, since mixed read/write patterns expose write-amplification issues that pure tests miss.

Network: Throughput, RTT, and Packet Path

Measure both directions against a well-provisioned endpoint. iperf3 -c <host> -P 4 -t 30 with four parallel streams will approach the interface limit; a single stream often underreports because of TCP window and RTT effects. Run iperf3 -R for the reverse direction, since egress shaping is common on low-tier plans. Compare achieved throughput to the plan’s stated port speed — 60% is normal for a long RTT, under 30% usually means shaping.

Then measure latency consistency, which matters more than peak throughput for API servers. mtr -rwzbc 100 <target> reports loss and per-hop jitter; look for loss above 0.5% or hop-level variance above 5 ms. For syscall-level network cost, run netperf -H <host> -t TCP_RR -l 30, which reports transactions per second and mean latency for a request-response pattern — the closest synthetic proxy for RPC traffic. Check ethtool -S eth0 | grep -i drop afterward for silent packet drops.

Recording Results So They Stay Comparable

Store each run’s raw output with the environment metadata in a simple directory structure, for example bench/2026-05-01/cpu.txt, including firmware and kernel versions. Use a fixed command set and never change parameters mid-series. A short shell script that runs all four suites and appends a JSON line per run makes this repeatable and lets you diff two instances mechanically. Include the plan tier in the filename so results from a dedicated-resource plan versus shared hosting are never compared by accident.

Re-run the suite quarterly and after any provider maintenance or live migration. A 20% drop in disk IOPS or a jump in network RTT with no change on your side is a placement or contention signal, and it is much easier to argue with support when you have the before-and-after data in hand.

Red Flags That Justify a Plan Change

Four patterns recur across bad instances: sustained steal above 3% with your own load under 60%, random write IOPS falling below 2k, p99 disk latency above 20 ms on an “SSD” plan, and asymmetric network throughput more than 4× between directions. Any one of these caps what you can achieve regardless of application tuning, and none of them can be fixed from inside the guest.

Distinguish transient from structural problems by running the suite at three different hours. If the numbers are stable across the day, the degradation is structural — a quota or an oversubscribed host. If they collapse only during local business hours, you are seeing neighbor traffic and can either shift batch work or move to a plan with dedicated CPU entitlement.

From Baseline to Production Monitoring

A benchmark is only useful if it becomes a reference. Export the same metrics — steal time, disk p99, network RTT — into your monitoring system and alert on deviation from the benchmark baseline rather than on absolute thresholds, which differ per host. Prometheus node_exporter covers most of this via node_cpu_seconds_total{mode="steal"} and node_disk_io_time_seconds_total.

Remember that synthetic numbers bound what is possible; they do not predict application throughput. Treat the suite as a diagnostic instrument that tells you which resource is the wall, then optimize the application against that wall with real traffic.

Summary: Four Numbers, One Controlled Method

CPU with steal and scheduler pressure, memory bandwidth, random-write disk latency, and bidirectional network RTT form a complete baseline. Record them cleanly, compare them mechanically, and re-run on a schedule. That discipline converts benchmarking from a one-off ritual into a reliable way to catch host-level degradation before it reaches users.

Leave a Reply