VPS comparison: benchmarks with fio, sysbench and iperf3

Comparing two VPSs on measurements rather than spec sheets comes down to one protocol applied to both machines: same parameters, same conditions, several passes. Measure the disk with fio, the CPU and RAM with sysbench, the network with iperf3, and record the steal time during every test. That is the core of any serious VPS comparison.
The protocol has five steps, repeated over three passes at different times of day for each machine. It complements a guide to choosing a VPS for developers, which looks at use cases rather than measurements.
In brief
- An objective comparison rests on three rules: same parameters, same conditions, several runs.
- Both VPSs have the same OS, the same kernel, the same number of vCPUs and the same RAM.
- Run three passes at different times of day and keep the median.
- fio with direct I/O measures IOPS and latency, sysbench measures CPU and memory, and iperf3 measures the network.
- Steal time is recorded during every test, because it is what gives away overselling.
Setting up a reproducible VPS comparison protocol
Start by freezing everything that can vary: same distribution (Ubuntu 24.04 LTS or Debian 12), same vCPUs, same RAM, a freshly installed VPS and no running services. Never run these tests on a production machine, because fio writes continuously and will saturate the disk.
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt install -y fio sysbench iperf3 sysstat mtr-tiny
lscpu | grep "Model name"
uname -r
Run fio against a test file, never against the raw device (/dev/vda), or you risk destroying data. Windows Server needs different tools and tests, so it deserves its own set of criteria.
Copy this log sheet for each pass:
| Field | What to record |
|---|---|
| Date and time | e.g. Tuesday, 9 pm |
| CPU model, vCPUs, RAM | output of lscpu |
| Kernel and versions | uname -r, fio --version, sysbench --version, iperf3 --version |
| Results | IOPS, p99, events/s, %steal, Mbit/s |
Measuring disk IOPS and latency with fio
The three commands below use the parameters described in the official fio documentation. --direct=1 bypasses the operating system's page cache: without it, you would be measuring RAM. libaio is Linux's native asynchronous I/O engine, and io_uring is a more recent alternative. It does not matter which one you pick, as long as you use the same one on every VPS.
# 1. Random read, 4k
fio --name=randread --filename=/var/tmp/fio-test --rw=randread --bs=4k \
--iodepth=32 --numjobs=1 --direct=1 --ioengine=libaio --size=4G \
--runtime=60 --time_based --group_reporting
# 2. Random write, 4k
fio --name=randwrite --filename=/var/tmp/fio-test --rw=randwrite --bs=4k \
--iodepth=32 --numjobs=1 --direct=1 --ioengine=libaio --size=4G \
--runtime=60 --time_based --group_reporting
# 3. Sequential, 1M blocks (mixed read and write, throughput in MB/s)
fio --name=seq --filename=/var/tmp/fio-test --rw=rw --bs=1M \
--iodepth=8 --numjobs=1 --direct=1 --ioengine=libaio --size=4G \
--runtime=60 --time_based --group_reporting
rm /var/tmp/fio-test
Why 60 seconds and 4 GB? Many VPS plans allow a burst: high IOPS for a few dozen seconds, then a ceiling. A 10-second test captures the burst, not the performance you can sustain. If your RAM is larger than 4 GB, raise --size above the amount of RAM.
In the output, read IOPS, bw (bandwidth) and above all the clat (completion latency) percentiles: p99 and p99.9. Unstable p99 latency matters more than a peak IOPS figure, because it is what a database or a game server actually feels.
Testing CPU and memory with sysbench
The sysbench CPU test calculates prime numbers up to --cpu-max-prime, which defaults to 10000. We use 20000. Whatever value you choose, keep it identical everywhere: scores cannot be compared across different values.
sysbench cpu --cpu-max-prime=20000 --threads=1 --time=30 run
sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) --time=30 run
sysbench memory --memory-block-size=1M --memory-total-size=10G run
Note the events per second. The useful figure is the multi-thread score divided by the product of the single-thread score and the number of vCPUs, that is multi / (single × vCPUs). Close to 1, the vCPUs are genuinely available. Well below (our working rule: under 0.7), they are shared or throttled.
There is no universal "normal" score, since it depends on the CPU generation, which is why we run lscpu. Compare two VPSs with the same processor model. Otherwise the gap reflects the hardware, not overselling.
Measuring network throughput with iperf3
iperf3 works in client/server mode. On the remote machine, run iperf3 -s. According to the iperf3 manual, the server listens on TCP port 5201 by default. Open that port in the firewall for the client's IP only, then close it again after the test.
sudo ufw allow from CLIENT_IP to any port 5201
iperf3 -s # server side
iperf3 -c SERVER_IP -t 30 -P 4 # TCP, 4 parallel streams
iperf3 -c SERVER_IP -t 30 -P 4 -R # reverse direction
iperf3 -c SERVER_IP -u -b 500M -t 30 # UDP: jitter and packet loss
With UDP, the -b option is essential: the default target bandwidth is only 1 Mbit/s. This mode matters most for gaming and VoIP, where jitter and packet loss count for more than raw throughput.
A result close to the port's nominal speed, once TCP/IP header overhead is deducted, is healthy. On a 1 Gbit/s port that works out at roughly 940 Mbit/s (calculated from Ethernet frame size). Throughput also depends on the network path and on the remote machine, so use two VPSs from your own estate rather than a public server that may be congested. For latency, add ping and mtr from the region where your users are.
Detecting steal time and overselling
Steal time is the time your vCPU is ready to run but is waiting for the hypervisor to give it a physical core. The kernel counts it in /proc/stat, which is described in the Linux kernel documentation. You will see it in the %st column of top, %steal in mpstat and st in vmstat. At source, it is the eighth value on the cpu line of /proc/stat, which is $9 in awk, since $1 is the word "cpu".
The method: run sysbench continuously and record steal alongside it. As /proc/stat holds counters accumulated since boot, note the value before and after the test: only the difference reflects the steal during the test.
awk '/^cpu /{print "steal before (ticks) =", $9}' /proc/stat
sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) --time=120 run &
mpstat -P ALL 5 20
wait
awk '/^cpu /{print "steal after (ticks) =", $9}' /proc/stat
Three signals point to overselling: sustained steal above a few per cent (5 to 10% is often quoted), high variance between passes or by time of day, and IOPS that collapse after the burst. If these signals persist after a plan change, or if your workload exceeds what a VPS can offer, take a close look at moving from a VPS to a dedicated server. KVM virtualisation with dedicated resources limits the problem.
Typical results and how to interpret them
These values are orders of magnitude and working decision rules, to be checked against your own measurements. They are not a commercial promise.
| Test | Metric | Healthy value | Warning sign |
|---|---|---|---|
| fio randread 4k | IOPS, p99 latency | IOPS steady over 60 s and close to the advertised ceiling; p99 of the same order as the median | More than 20% gap between passes; p99 ten times the median |
| fio randwrite 4k | IOPS, p99 latency | Same, with writes slower than reads | Collapse after the burst |
| fio sequential 1M | MB/s | Constant throughput, stable from one pass to the next | Throughput halves during the test |
| sysbench 1 thread and N threads | events/s | Ratio multi/(single × N) close to 1 | Ratio below 0.7 |
| Steal | %st / %steal | Close to 0% | More than 5% sustained |
| iperf3 TCP | Mbit/s | Close to nominal minus overhead (≈ 940 on 1 Gbit/s) | Highly variable throughput or below 70% of nominal |
| iperf3 UDP | Jitter, packet loss | No loss, very low jitter | Recurring loss, jitter of several ms |
Our decision rule: at a similar price, choose stability (low variance, steal close to 0), not the peak.
Mistakes that skew a comparison
- Running a single test, when you need at least three.
- Forgetting
--direct=1. - Using a test file that is too small, so you measure the cache.
- Comparing VPSs with different numbers of vCPUs.
- Running iperf3 against a congested server.
- Testing while the system is updating.
- Reading the average instead of the percentiles.
Choosing a VPS on measurable criteria
Before testing, ask for the following in writing: the virtualisation type (KVM), the overselling policy, the network port speed, the storage type, the datacentre location (latency to your users, data protection requirements) and the DDoS protection. Our high-performance Linux VPS answers each of these points: KVM virtualisation with no overselling, SSD storage, a 10 Gbit/s network with France-IX peering, hosting at the Telehouse Paris datacentre (Tier 3+), 1 Tbit/s Anti-DDoS included and no minimum term. It can therefore go through the same protocol. The plans are currently available to pre-order, with availability announced for 1 November 2026, and we will not publish any score until we have measured something.
Frequently asked questions
How do I objectively compare the performance of two VPSs?
Use the same OS, the same kernel, the same vCPUs and the same RAM, then run the same fio, sysbench and iperf3 commands. Do three passes at different times of day and compare the medians, not the best results.
How do I measure a VPS's IOPS with fio?
Run fio with --rw=randread --bs=4k --iodepth=32 --direct=1 --ioengine=libaio --runtime=60 --time_based on a 4 GB test file. Read the IOPS line and the p99 and p99.9 percentiles of clat.
What is a normal sysbench CPU score for a VPS?
There is no universal score: it depends on the CPU model and on the value of --cpu-max-prime (10000 by default). Compare two VPSs with the same processor and the same parameters, and check that the multi-thread score comes close to the single-thread score multiplied by the number of vCPUs.
How do I detect steal time and overselling on a VPS?
Run sysbench cpu continuously and read %st in top or %steal in mpstat -P ALL 5. Sustained steal above 5% (some people use 10%), or steal that varies sharply by time of day, points to overselling.
How do I test a VPS's network throughput with iperf3?
Run iperf3 -s on one machine, then iperf3 -c IP -t 30 -P 4 on the other. Add -R for the reverse direction. Port 5201 must be open in the firewall.
How many times should I run a benchmark for it to be reliable?
Three passes at the very least, at different times of day (for example morning, afternoon and evening), keeping the median. If the gap between passes exceeds 20%, the VPS is unstable: add more passes before drawing a conclusion.
Running the protocol on your next VPS
Copy the log sheet and run the first pass on your current VPS: you will have a hard number to work from before you compare anything. Then pre-order our high-performance Linux VPS and test it with this protocol as soon as it becomes available on 1 November 2026.