Verifying a VPS Network: iperf3 Throughput Tests, mtr Route Readings and Disk Floors

Every provider’s order page sells the same four numbers: vCPU count, RAM, disk size and port speed. Three of those four are measurable in under ten minutes with tools that ship in every distro’s repositories. The fourth — port speed — is the number that is most often rounded up, and the one most buyers never check.

This is a measurement protocol, not a list of tools. Each test has a stated pass criterion, so the output is a verdict rather than a screenshot of a number. If you have not decided which tier to rent yet, our VPS plan sizing guide by workload explains what each spec tier is actually for; this page covers how to verify you received it.

What Each Test Can and Cannot Prove

TestMeasuresCan proveCannot prove
iperf3 TCP single streamBulk throughputPort speed floor, shapingSmall-packet performance
iperf3 -P 8Aggregate throughputWhether a quota is per-flowPer-connection latency
iperf3 -u -bLoss and jitter at a rateUDP shaping, policer dropTCP fairness behaviour
ping -f / flentLatency under loadBufferbloat depthRoute quality
mtr -rwzcPer-hop loss and RTTWhere loss enters the pathWhy that hop is slow
sysbench cpuSingle-core speedCPU generation classSustained multi-core (needs 5 min)
fio with --iodepth=324K IOPS at depthProvisioned IOPS tierCross-tenant variance over hours

Step 1: Establish the Reference Endpoint

Throughput between your VPS and an arbitrary public server measures the internet, not the VPS. Use a second host you control, or accept that public iperf3 servers are shared and often rate-limited. Pick a target within 200 km of the provider’s region — a transatlantic test cannot validate a 1 Gbps port because a single TCP stream will not fill it.

# On the reference host
apt install -y iperf3
iperf3 -s -p 5201 --forceflush

# On the VPS, start with the baseline
sudo apt install -y iperf3 mtr-tiny sysbench fio
iperf3 -c ref.example.net -p 5201 -P 1 -t 20 -O 3 -J > /tmp/iperf_p1.json

-O 3 skips the first three seconds, which are dominated by slow start and will make a healthy link look 20% short. -J gives you JSON so you can compare runs rather than eyeballing Mbit/s.

The pass criterion: single-stream throughput within 80% of the advertised port speed, and no more than 15% variance across three runs. A 1 Gbps port that delivers 620 Mbit/s per stream usually indicates a 700 Mbit/s shaping ceiling or a CPU-bound iperf3 on a small instance — not necessarily a dishonest provider. Repeat with -P 4 before drawing a conclusion.

Step 2: Separate “Per-Flow Limit” from “Total Limit”

This is the single most useful network test on a cheap VPS. Many providers police per-flow rather than in aggregate. If 1 stream gives 250 Mbit/s and 8 streams still give 250 Mbit/s, you have a total cap. If 8 streams give 1,000 Mbit/s, you have a per-flow policer and your bulk transfer tools should use more than one connection.

for p in 1 4 8; do
  iperf3 -c ref.example.net -P $p -t 20 -O 3 -f m | awk -v p=$p '/sender/{print "P="p, $7, $8}' | tail -1
done
P=1 resultP=8 resultInterpretationAction
940 Mbit/s945 Mbit/sGenuine 1 Gbps portNone
250 Mbit/s260 Mbit/sHard aggregate capTune for latency, not bulk
250 Mbit/s900 Mbit/sPer-flow policerParallelise backup and migration transfers
400 Mbit/s410 Mbit/sCPU-bound at ~400 Mbit/sCheck top: iperf3 at 100% of one core

The last row is common on 1-vCPU instances. iperf3 in userspace saturates a single core at roughly 500–700 Mbit/s depending on MTU and whether checksums are offloaded. Before blaming the provider, confirm with mpstat -P ALL 1 5 that you were not CPU-bound on the test itself.

Step 3: Prove or Disprove a UDP Policer

Providers that advertise “1 Gbps” while selling a 100 Mbps plan often rate-limit UDP differently from TCP. Run a fixed-rate UDP test and read the loss column; a policer shows up as loss that climbs in a step at a specific rate.

for b in 50M 150M 400M; do
  echo "=== offered $b"
  iperf3 -c ref.example.net -u -b $b -t 10 -O 2 | grep -E 'receiver|Lost'
done

Zero loss at 400 Mbit/s offered with 380 Mbit/s delivered means no UDP policing below 400. Loss appearing between 150 M and 400 M locates the ceiling within that band. Bisect once more to narrow it — the exact number is useful evidence if you need to open a support ticket.

Step 4: Measure Latency Under Load, Not Idle

A link that pings at 12 ms idle and 180 ms while saturated is worse than one that pings at 30 ms idle and 35 ms loaded, for anything interactive. Run the RTT measurement concurrently with the bulk transfer.

# Terminal 1: saturate the link
iperf3 -c ref.example.net -t 60 -P 4 >/dev/null

# Terminal 2: sample RTT during saturation
ping -i 0.2 -c 150 ref.example.net | tail -3

Compare mdev and max RTT against the idle baseline. A jump of more than 5× indicates a deep buffer on the path with no active queue management. fq_codel on the VPS’s egress can help your own outbound traffic, but it cannot fix bufferbloat in the provider’s uplink — that is a reason to change providers, not a tuning task.

Step 5: Read the Route with mtr, Correctly

The standard misreading of mtr is treating loss at an intermediate hop as a fault. Many routers deprioritise ICMP TTL-expired replies to their own address while forwarding traffic perfectly. The rule: only loss that persists to the final hop matters.

mtr -rwzc 200 -i 0.2 ref.example.net
  • -r report mode, -w wide, -z show AS numbers, -c 200 enough samples for a stable loss figure.
  • Loss at hop 4 of 8% and 0% at hop 8: the hop is rate-limiting ICMP. Ignore it.
  • Loss that starts at hop 6 and continues at 8: real loss on that segment. Note the AS on hop 6 and 7 — that is who to talk to.
  • RTT that steps up 40 ms at a hop and stays up: a real long-haul segment, not congestion. Check whether it matches the physical distance.

Step 6: The Disk and CPU Floor

Network numbers mean little if the instance cannot feed the NIC. Two short tests set the floor. Run sysbench cpu for a full five minutes to catch the burst-credit collapse that many providers apply to the first minute of CPU usage.

sysbench cpu --threads=1 --time=300 run | grep 'events per second'
fio --name=randread --ioengine=libaio --direct=1 --bs=4k --iodepth=32 \
    --rw=randread --size=1G --runtime=60 --time_based --group_reporting

Compare the first 60-second window against the fifth. A drop of more than 30% in events per second means you are on a burst plan, and the sustained number is the one to plan capacity around. For fio, look at the clat p99 rather than the average — a 4K read average of 0.4 ms with a p99 of 9 ms describes a shared backing store with a noisy neighbour on it.

Recording the Result So It Means Something in Six Months

Store one line per run in a file you keep outside the VPS, so it survives a provider migration:

echo "$(date -Is),$(hostname),$(iperf3 -c ref.example.net -P 4 -t 15 -O 3 -f m | awk '/sender/{print $7}'),\
$(mtr -rwzc 100 -i 0.2 ref.example.net | tail -1 | awk '{print $6}')" >> ~/baseline.csv

Re-run quarterly and after every plan change. The value is in the delta. Our VPS benchmark reference lists the ranges these tests should land in per tier.

Leave a Reply