Two plans both read “2 vCPU / 4 GB” and differ by 3× in CPU steal and 10× in disk latency at the same price point. The rental market sorts into three real tiers — entry, mid, and dedicated-core — and the differences that matter are almost never on the order page. What is on the order page is the vCPU count, which is the number least predictive of performance.
This article breaks the tiers apart by what is actually provisioned behind each label, then gives a measurement protocol for the first hour of a trial so the decision is made on counters instead of marketing copy. Every test here runs on a stock Ubuntu 24.04 image with packages from the default repositories.
The three variables that separate price tiers
Price correlates with three allocation policies: CPU scheduling (is your vCPU pinned to a physical core or time-sliced?), memory commitment (is your RAM reserved or overcommitted?), and I/O guarantees (is the disk local NVMe, network-attached, or capped by a token bucket?). A $5 tier typically shares all three; a $40 tier typically fixes at least two.
| Tier | CPU model | Typical steal under load | Disk | Failure mode |
|---|---|---|---|---|
| Entry (~$5) | Shared threads, 4–8× oversubscribed | 3–15% | Network volume or SATA SSD | Latency spikes at peak hours |
| Mid (~$15) | Shared cores, 2× oversubscribed | 1–5% | Local NVMe, burstable IOPS | Throttle after burst credits |
| Dedicated-core (~$40) | Pinned physical cores | < 1% | Local NVMe, no cap stated | Nothing visible; verify anyway |
These ranges come from repeated measurement on production KVM nodes, not from spec sheets. The overlap is wide — a well-run entry tier can beat a badly-run mid tier — which is exactly why the trial-hour protocol matters more than the tier label.
Measure steal time first
CPU steal is the percentage of wall-clock time your vCPU was runnable but the host scheduled someone else. It is reported in /proc/stat and top and is the fastest way to classify a plan. Sample it while the guest is under load, not idle: an idle guest reports near-zero steal regardless of how oversubscribed the host is.
# baseline: 60 seconds of full-load on every vCPU, measure steal
nproc
for i in $(seq $(nproc)); do
timeout 60 sha256sum /dev/zero > /dev/null &
done
vmstat 5 12
wait
Read the st column. Under 1% sustained means the host is not contended. Between 1% and 4% is normal for a shared plan at peak. Above 5% for the whole minute means the host is selling more vCPU than it can deliver during your measurement window, and the same contention will show up as p99 latency in production.
awk '/^cpu /{printf "user=%s nice=%s sys=%s idle=%s iowait=%s irq=%s softirq=%s steal=%s\n",$2,$3,$4,$5,$6,$7,$8,$9}' /proc/stat
# steal as a percentage over a 10s window
grep '^cpu ' /proc/stat > /root/s1; sleep 10; grep '^cpu ' /proc/stat > /root/s2
python3 - <<'PY'
a=open('/root/s1').read().split()[1:]; b=open('/root/s2').read().split()[1:]
a=[int(x) for x in a]; b=[int(x) for x in b]
d=[y-x for x,y in zip(a,b)]; t=sum(d)
print(f"steal {100*d[7]/t:.2f}% iowait {100*d[4]/t:.2f}%")
PY
Confirm what the CPU actually is
vCPU count says nothing about generation. A 2 vCPU allocation on a 2013 Xeon E5-2670 delivers roughly 40% of the single-thread throughput of the same count on a recent EPYC, which matters enormously for PHP, PostgreSQL and anything single-threaded. Two commands settle it.
lscpu | egrep 'Model name|MHz|Hypervisor|Flags' | cut -c1-120
# single-thread throughput baseline
sysbench cpu --cpu-max-prime=20000 --threads=1 --time=20 run | grep 'events per second'
# all-cores
sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) --time=20 run | grep 'events per second'
Reference points on verified hardware: a modern EPYC or Xeon Scalable core scores 1,800–2,600 events/s single-threaded on this workload; 2016-era Skylake around 1,100–1,400; anything under 900 is a decade-old core or a heavily throttled guest. Compare single-thread to all-core scaling too — if four threads deliver only 2.5× the single-thread number, the guest is sharing its physical cores.
Disk latency, not IOPS
Providers quote IOPS because it is a big impressive number. Latency at low queue depth is what users feel. A single 4k random read at queue depth 1 exposes whether the disk is local or sitting behind a network round trip.
fio --name=qd1 --filename=/root/t.bin --size=1G --direct=1 \
--rw=randread --bs=4k --iodepth=1 --runtime=30 --time_based \
--ioengine=libaio --output-format=json \
| jq '.jobs[0].read | {iops: (.iops|floor), lat_us: (.clat_ns.mean/1000|floor)}'
- < 100 µs (over 10,000 IOPS at QD1) — local NVMe, good tier
- 100–250 µs — local SSD or a fast network volume
- 250–600 µs — network-attached block storage, acceptable for web serving
- > 1 ms — either heavily contended or remote storage; databases will suffer
Then check whether the number holds. Run the same test for five minutes and watch the interval log; a device that drops to a third of its starting IOPS after 30 seconds has a burst credit system, and the sustained figure is the real product. If you want the full protocol including the sustained write and sequential tests, our performance benchmarking reference covers the sequence with pass criteria.
Memory: check what is committed, not what is allocated
Overcommitted RAM is invisible until you touch it. Allocate your full allowance and check whether the kernel can keep it resident while another workload runs.
free -m
# allocate 80% of RAM and verify it stays resident
python3 -c "
import os
n = int(0.8 * $(free -m | awk '/^Mem:/{print $2}'))
b = bytearray(n * 1024 * 1024)
print('allocated', n, 'MiB; touching')
for i in range(0, len(b), 4096): b[i] = 1
print('resident, still holding')
import time; time.sleep(30)
"
# in another shell during the hold:
awk '/MemAvailable|SwapTotal|Committed_AS|MemTotal/' /proc/meminfo
A healthy instance keeps the allocation resident without swap activity. If si/so in vmstat 5 show sustained non-zero values while you hold 80% of the advertised RAM, the plan is overcommitted at the host level or the guest was provisioned with less memory than sold — check dmidecode -t 17 against the order page.
Network: the number most often rounded up
Port speed is advertised far more often than it is delivered. A 1 Gbps port that sustains 300 Mbps during peak hours is common on entry tiers, and for a busy API or media origin it is the binding constraint. Two tests, five minutes:
# throughput to a known-good public iperf3 endpoint
iperf3 -c speedtest.example.net -p 5201 -t 30 -P 4
# across the instance's own NIC, excludes the WAN path
iperf3 -s -D && iperf3 -c 127.0.0.1 -t 10
# retransmits and RTT variance over 300 packets
mtr -rwzc 300 1.1.1.1 | tail -5
For the local loopback test, expect several Gbps on modern virtio — it exercises the guest’s network stack, not the wire. The public endpoint test is the one that reflects your users. Anything below 70% of the advertised port speed during business hours deserves a support ticket, and if mtr shows loss appearing at a specific hop that is not yours, it is an upstream congestion problem no plan change will fix.
What each tier buys you in workload terms
The tier decision reduces to which resource your workload saturates first. Static sites and small APIs saturate page-cache misses, so disk latency at QD1 decides everything. Compilers, CI runners and video encoders saturate CPU, so generation and steal decide. Databases and mail servers saturate the write path on a 5-minute timescale, so the sustained write floor decides. Only the third group genuinely benefits from dedicated-core pricing — the other two usually pay for isolation they never use.
- Buy entry when the workload is latency-tolerant and mostly cache-served: blogs, static sites, small bots, DNS resolvers.
- Buy mid when you need local NVMe and predictable single-thread performance: typical LAMP/LEMP apps, small PostgreSQL, GitLab runners.
- Buy dedicated-core when your p99 latency target is set by a database doing more than ~500 TPS, or when build times are the product.
If you want to see how these tiers map onto specific plans and storage types, the plan comparison on the main site lists virtualization type and disk backing per tier, which is the information most order pages omit. For sizing the workload itself, the workload sizing guide maps RAM and vCPU requirements by service type.
The first-hour trial script
Execute these six checks in order within the refund window, and write the numbers down. Order matters: CPU before disk, because a contended host distorts disk results.
lscpuand a single-thread sysbench — identify the CPU generation and per-core throughput.- 60-second full-load
vmstat— classify steal. Above 5% sustained, stop and ask for a different node. - QD1 4k random read — classify storage as local or remote.
- 5-minute random write with interval logging — detect burst credits and find the sustained floor.
- 80% RAM allocation — detect host overcommit through swap activity.
- iperf3 to a public endpoint at peak hours — verify port speed under contention.
Six numbers, about an hour of wall clock including the 5-minute write test, and a decision based on the same counters a provider’s own engineers look at when they investigate a complaint. The plan description is a hypothesis; these tests are the evidence.



Leave a Reply
You must be logged in to post a comment.