Two servers with identical spec sheets — same vCPU count, same RAM, same NVMe — routinely differ by a factor of three on real work. The spec sheet describes what was allocated. It says nothing about how many other tenants were allocated the same silicon, whether the disk is a local drive or a network volume three switches away, or whether your vCPU spends a fifth of every second waiting for a hypervisor that promised it to somebody else. Benchmarking is how you find out, and the whole sequence takes about twenty minutes.
The four measurements that decide a server
Everything worth knowing about a machine's speed falls into four buckets. Most published comparisons measure the first and the last, skip the two in the middle, and then wonder why the fast-looking server feels slow.
Sequential CPU
How fast a single thread runs. It sets the ceiling for a web request, a query plan, a build step. Most applications hit this wall long before they hit the one made of core counts.
Contention
How much of your allocated CPU another tenant is actually consuming. Invisible on a spec sheet, measurable in one command, and the entire difference between two identical-looking plans.
Storage latency
Not how many IOPS the array can do in aggregate, but how long one small synchronous write waits. That is what a database commit and a session save sit on.
Network path
Throughput in both directions, plus the route and the packet loss between your instance and the people who use it. A port speed is a claim; a path is a measurement.
Before measuring any of them, spend two minutes establishing what you were actually sold. It changes how you read every number that follows.
Before the first test: find out what you actually bought
A four-core container under a CPU quota and a four-core KVM instance with dedicated threads answer the same command with very different numbers, and only one of the two is what most plan pages imply. Establish which you have before you start drawing conclusions from throughput figures.
- 1
Identify the virtualization
systemd-detect-virt prints kvm, lxc, openvz or none. KVM is full virtualization and none is bare metal. lxc and openvz mean you share a kernel with other tenants: you cannot load modules, cannot run your own kernel, and are usually subject to a limit the guest cannot see. That distinction matters more than any two-dollar difference in monthly price.
- 2
Read the CPU you were given
lscpu shows the model, the thread count, the hypervisor flag and the cache sizes. grep -m1 'model name' /proc/cpuinfo names the physical part, which tells you the generation you are actually renting rather than the one in the marketing copy. Write it down; it is the context for every CPU number below.
- 3
Look for a hidden cap
On cgroup v2, cat /sys/fs/cgroup/cpu.max prints a quota and a period. A value of max 100000 means uncapped; 200000 100000 means you have two cores' worth no matter how many you can see. A guest that reports eight cores and is capped at two produces a perfectly normal single-thread score and an all-core score that stops scaling at three.
- 4
Confirm the rest of the inventory
nproc, free -h, lsblk and ip -br a take five seconds between them and catch the honest mistakes: the disk that is 40 GB instead of 60, the missing IPv6 block, the second address that was never attached.
Do all of this on a fresh instance, before you install anything on it. Once the box has a database and a workload, every benchmark measures the workload as much as the hardware, and you will never get a clean baseline again.
The rules that make a benchmark mean something
Most published VPS benchmarks are worthless, and for a boringly consistent reason: one run, on one day, with the best number kept. A shared machine does not have a performance level. It has a distribution, and the distribution is the product you are buying.
- Run every test at least three times and keep the median, not the best. The best number tells you what the hardware can do when nobody else wants it. The median tells you what you bought.
- Test at two different hours — one in the local afternoon of the region, one in its quiet early morning. If the two disagree by more than roughly twenty percent, the node is contended, and the afternoon figure is the one that describes your life.
- Give every test at least sixty seconds. Short bursts hide throttling: plenty of providers allow full speed for the first ten to thirty seconds and then quietly apply a credit system.
- Pin the tool versions. sysbench 1.0.20 and sysbench 1.1 do not produce comparable numbers, and neither do fio builds with different IO engines. Record the version next to every result or the comparison means nothing.
- Benchmark an idle box. No package manager running, no backup job, no monitoring agent you forgot about. uptime should show a load average near zero before you begin.
- Note the date, the region and the plan alongside the numbers. Six months from now you will want to know whether the node got slower or you simply landed on a different one.
Sustained full-load loops on shared infrastructure look exactly like abuse to an automated system. Reasonable benchmarking is permitted by almost every acceptable use policy; a week of pegged cores is not. Run the tests for as long as you need and then stop.
CPU: single-core, all-core, and the gap between them
Two numbers matter here, and the relationship between them matters more than either one alone. Single-thread throughput sets the ceiling for anything sequential. All-thread throughput sets the ceiling for anything parallel. An instance with genuinely dedicated threads scales almost linearly between the two. An oversold one does not, and the ratio is where it gives itself away.
- 1
Measure one thread
sysbench cpu --cpu-max-prime=20000 --threads=1 --time=60 run. Record the events per second. This is your reference for everything sequential — a PHP request, a single query, the front end of a compiler — and it is the number that a higher clock actually buys you.
- 2
Measure every thread
Repeat with --threads=N, where N is the core count you were sold. On dedicated threads the result lands close to N times the single-thread figure. If four threads deliver less than two and a half times one thread, the cores are shared, capped, or both.
- 3
Cross-check against real work
openssl speed -evp aes-128-gcm exercises the AES-NI path that TLS actually uses, and 7z b reports a compression rating that stresses cache and memory latency rather than raw arithmetic. When two tools disagree wildly, one of them has usually hit a limit the other did not.
Absolute values only mean something against a reference you trust, so build one: run the same commands on a machine you already know — your laptop, an existing server — and compare. A current EPYC or Ryzen core sits in roughly the same class as a good desktop core from the last few years. If a premium-priced VPS scores half of a four-year-old laptop on a single thread, you are on an old platform, a contended one, or both.
vCPU counts are not comparable across providers and never have been. One provider's vCPU is a dedicated SMT thread; another's is a slice of a core shared with eleven other tenants. Events per second is comparable. Insist on the measurement, not the label.
Steal time: the number that exposes an oversold host
Steal time is the share of time your virtual CPU was ready to run and could not, because the hypervisor was running somebody else instead. It is the most direct measurement of overselling available to a guest, it costs one command, and almost nobody looks at it.
- 1
Watch it live
vmstat 1 10 prints a column headed st. top shows the same value as %st on the CPU line. Both are percentages of total CPU time, averaged across all of your cores, which means a small figure spread across many cores is still a large amount of missing work.
- 2
Break it down per core
mpstat -P ALL 1, from the sysstat package, shows %steal for each vCPU separately. Steal concentrated on one core is usually a scheduling artefact. Steal spread evenly across all of them means the node itself is oversubscribed.
- 3
Measure it under load, never at idle
An idle guest steals nothing, because it asks for nothing. Run the all-core sysbench test in one terminal and vmstat 1 in another; the steal figure during that minute is the one that describes your server.
One honest caveat: some hypervisors do not report steal to the guest at all, and a container does not report it in any meaningful way. Zero steal on an OpenVZ or LXC instance is not evidence of a quiet node — it is evidence that the number is unavailable. Where steal is missing, measure variance instead. Run the same sixty-second CPU test ten times in a row and look at the spread: a machine that returns the same figure every time has the cycles, and a machine whose figure wanders by thirty percent is sharing them, whatever it does or does not report.
Disk: latency is the number, not IOPS
Storage is where VPS benchmarks go most wrong, because the two habits everybody has — running dd, and quoting a headline IOPS figure — both measure something other than what a real workload feels.
dd if=/dev/zero writes a stream of zeros. On thin-provisioned, compressing or deduplicating storage, zeros are close to free, so the resulting number describes a compression algorithm rather than a drive. Without oflag=direct it also writes into the page cache and reports the speed of your RAM. It is a fine way to confirm that a disk exists and a poor basis for deciding whether to pay for it.
Use fio instead, and read the latency percentiles rather than the headline. A database commit does not care that the array can do four hundred thousand IOPS in aggregate. It cares how long one fsync takes at the ninety-ninth percentile, on a busy evening, while your neighbours are also committing.
- 1
Random read — the general-purpose number
fio --name=randread --filename=fiotest --size=4G --rw=randread --bs=4k --ioengine=libaio --direct=1 --iodepth=32 --numjobs=4 --runtime=60 --time_based --group_reporting. Record the IOPS and, more importantly, the clat percentiles it prints underneath them.
- 2
Random write — the honest one
The same command with --rw=randwrite. Writes are where shared storage and cheap consumer drives come apart, and where a provider's caching layer either saves them or does not. Expect a gap between read and write figures; expect a chasm only on bad storage.
- 3
Commit latency — the one that decides your database
fio --name=fsync --filename=fsynctest --size=1G --rw=write --bs=4k --ioengine=sync --fdatasync=1 --runtime=60 --time_based. The fsync latency percentiles are, for anything transactional, the single most important storage number on the page. Enterprise drives with power-loss protection answer in microseconds; a network volume answers in milliseconds.
- 4
Sequential throughput — last, and least
Add --rw=read --bs=1M --iodepth=16 --numjobs=1 to learn what a restore or a large backup will do. This is the number providers advertise and the one that matters least on an ordinary day.
- 5
Clean up after yourself
Delete the test files when you are done. fio writes real data, and a --size chosen carelessly will fill a 20 GB plan while you are still reading the output.
Add --status-interval=10 and watch the run instead of the summary. An average across sixty seconds hides a cliff; ten-second slices show it clearly. A disk that halves its throughput thirty seconds in is a disk with a credit system, and your nightly backup is going to find the bottom of it.
Memory: bandwidth, and what the capacity figure hides
Capacity is the spec that gets sold; bandwidth is the spec that gets felt. It is also where a badly packed node shows itself, because memory controllers are shared: a neighbour streaming through a large dataset costs you real bandwidth even when your steal time looks respectable.
- sysbench memory --memory-block-size=1M --memory-total-size=64G --memory-oper=read run gives a rough transfer rate. It is a crude proxy, but it is available everywhere and comparable between runs on the same box, which is most of what you need.
- mbw 1024 or a STREAM build gives a better figure if you can install one. On a modern server platform, copy and triad results belong in the tens of gigabytes per second.
- Confirm the capacity is real: free -h should match the plan, and the si and so columns in vmstat 1 should sit at zero throughout a benchmark. A VPS that swaps under load has less usable memory than its plan page claims.
- Check for a balloon driver with lsmod | grep balloon. Ballooning lets a hypervisor reclaim memory from a running guest. It is a legitimate technique, and it is also the reason your available RAM can shrink without anyone sending you an email about it.
Network: throughput, latency, and the route you were not shown
A gigabit port is a claim about a cable. What you are actually buying is a path — from your instance, through the provider's uplinks and whatever transit they bought, to the people who use your service. Measure the path, not the port.
- 1
Throughput, in both directions
iperf3 -c <server> -P 8 -t 30 measures egress; add -R to measure ingress. Use eight parallel streams, because a single TCP stream over a long path is bounded by the bandwidth-delay product rather than by your port and will under-report badly. Test against two or three different endpoints: a public iperf3 server is shared infrastructure too.
- 2
Latency to the people who matter
ping for the baseline round trip, then mtr -rwzbc 100 <target> against networks your users are genuinely on. mtr exposes per-hop loss and jitter, which is how you tell a saturated uplink from a merely long path. Twenty milliseconds of RTT is worth more to a web application than doubling the port speed.
- 3
Sanity-check the port itself
A gigabit port tops out near 118 MB/s once protocol overhead is accounted for. Anything reporting more on a 1 Gbps plan is measuring to a local cache. Reaching 10 Gbps takes parallel streams and tuned buffers; a single-stream test will never show it to you.
- 4
Test at peak, not at dawn
Uplink contention is a time-of-day phenomenon. A 2 a.m. result from a datacentre in Amsterdam tells you very little about how the same rack behaves at 9 p.m. local time, which is when your users arrive.
Skip consumer speed-test tools for this. They pick a nearby node and report the best of several short bursts — a design intended to flatter a home connection, and one that says almost nothing about a server's transit quality. Read /locations alongside these numbers: the region you choose changes the route before any amount of tuning does.
Unmetered and unlimited are different claims, and neither is measurable with iperf3. Read the terms: a plan can be genuinely unmetered on volume and still shaped above a threshold. The benchmark tells you the speed. Only the policy tells you for how long you keep it.
Put it in one script and keep the raw output
The value of a benchmark compounds only if you can repeat it. Write the whole sequence into a single shell script that stamps its results with the date, the hostname, the region and the tool versions, then keep that script somewhere you will find it again — the same repository as your provisioning code is the obvious place.
- Run it on every new server within the first hour, before the box has a job. That is your baseline, and it is worth more to you than any published comparison ever will be.
- Run it again whenever something feels slow. Slow is a feeling; a median that dropped forty percent against last quarter's run is an argument, and support queues respond to arguments.
- Run it on a provider you are considering before you move anything to them. /guides has the full no-downtime migration sequence, and benchmarking the destination belongs at the start of that sequence rather than after the DNS has already moved.
- Keep the raw output, not a tidy summary. Six months later the percentile distribution will tell you something the single average you wrote down cannot.
Aggregate scripts exist that run most of this in one command, and the well-known one is genuinely useful for a quick comparison between hosts. It is also a remote script piped into a root shell on a box you are about to trust with something. Read it first, or run it on a machine you are willing to throw away. The convenience is real; so is the habit it teaches.
What a benchmark cannot tell you
These numbers cover one axis. A server that wins every test above can still be the wrong server, and the properties that decide that are not measurable with fio.
- Which law applies. The jurisdiction your data physically sits in decides who can compel its disclosure, and no benchmark has an opinion on the matter. /offshore-hosting covers what actually changes between countries, and it is usually more consequential than a twenty percent difference in IOPS.
- What the provider logs, and for how long. A fast node that retains six months of connection records is a materially different product from a slower one that retains none.
- What it took to buy. Whether an identity document and a card were required is a property of the purchase rather than the hardware — /no-kyc-vps and /pay-with describe the other model.
- How it behaves during an incident. Time to a first human reply, whether a null route is announced or silent, whether a snapshot genuinely restores. You learn these by using a provider or by asking pointed questions before you do.
- Whether today's number holds. A median is a measurement of today's neighbours. Re-run quarterly, because nodes get repacked and nobody sends a notice when yours does.
Benchmark before you commit, not after
The full sequence takes about twenty minutes and costs a few cents on hourly billing. That asymmetry is the whole argument for doing it: a rented hour is a far cheaper way to evaluate a host than a year of quietly tolerating one.
- 1
Buy the smallest instance in the region you want
Performance characteristics follow the node and the region far more than the plan size. A one-core instance in the same rack tells you most of what a larger one will do, for a fraction of the money, because the CPU model, the storage class and the network path are identical.
- 2
Run the full sequence twice
Once on arrival, once at the busiest local hour. Two honest runs on one instance is a much better signal than one run compared against five providers' marketing pages.
- 3
Measure the things that are not speed
Open a support ticket about something real and time the reply. Take a snapshot and restore it. Reboot from the panel and time that too. These are product measurements as much as IOPS is, and they are usually what decides whether you stay.
- 4
Then size the plan you will actually run
With a measured single-thread figure and a measured fsync latency in hand, sizing stops being guesswork. /guides has a sizing method that takes exactly those two numbers as its input.
Frequently asked questions
How long should each benchmark run?
Sixty seconds as a minimum, using the tool's time-based mode so the duration is a duration rather than however long a fixed amount of work happens to take. Anything shorter measures a burst allowance instead of a server. For storage in particular, run long enough to exhaust any credit system: if throughput is still steady at ninety seconds, it is real.
What counts as a good steal time?
Under half a percent is healthy, up to roughly two percent is ordinary on a busy shared node, and anything sustained above ten percent means you are queueing for a core you believe you own. Measure it while the all-core CPU test is running — an idle guest always reports close to zero, which is why idle readings are so often quoted.
Is dd a valid disk benchmark?
Only as a smoke test. Without oflag=direct it measures the page cache rather than the disk, and a stream of zeros is nearly free on any storage that compresses or thin-provisions. Use fio with --direct=1 and read the latency percentiles; the headline throughput figure is the least informative part of the output.
Why is my single-core score lower than the advertised clock suggests?
Three usual causes. The physical CPU is an older generation than the plan page implies; the vCPU is an SMT sibling sharing execution units with another tenant; or a cgroup quota is capping you. lscpu answers the first, the all-core scaling ratio answers the second, and cat /sys/fs/cgroup/cpu.max answers the third.
Can I trust a provider's published benchmarks?
Treat them as an upper bound. They were run on an idle node, at a quiet hour, with the best of several attempts kept — which is not dishonest, but it is not your Tuesday afternoon either. The only number that describes your server is the one you measured yourself, on the plan you are paying for.
Do high IOPS matter for a small website?
Far less than people assume. A modest site is bounded by single-thread CPU and by commit latency, not by aggregate IOPS. If you optimise one storage number, optimise the fsync percentile — that is what a database transaction and a session write actually wait on.
Should I benchmark a GPU server the same way?
The method transfers; the tools do not. VRAM capacity, memory bandwidth and the precisions the card supports decide throughput far more than the host CPU does, and the measurement that counts is tokens or images per second on the model you intend to run. /gpu-server and the GPU comparison in /guides cover that side. The CPU, disk and network tests here are still worth running, because a fast card behind a slow disk spends its life waiting on the dataset.
Does benchmarking violate the acceptable use policy?
Reasonable benchmarking does not, at any provider worth using — you are measuring a resource you rented. What draws attention is duration: an hour of tests is diagnostics, a week of pegged cores on a shared node is a workload you did not declare. Run what you need, keep the output, and stop.


