GICT knowledge base
How to Benchmark a VPS with fio and sysbench
Published
A VPS benchmark measures a particular workload under particular conditions. It can help compare configurations or investigate a slowdown, but a synthetic test does not identify every application bottleneck or prove that the underlying hardware caused a change.
This guide uses two standard tools. fio measures storage the way applications use it: many small random reads and writes, large sequential transfers, and the latency of each operation. sysbench measures CPU and memory speed.
Before you start
You need:
- An Ubuntu server with sudo access. The commands below use Ubuntu 24.04.
- About 5 GB of free disk space for the test file.
- About 10 minutes. The tests load the server heavily, so run them before you go live or during a quiet period, not at peak time on a production server.
The disk tests write to a test file. Never point fio at a disk device such as /dev/sda with a write test; that overwrites the file system.
Install the tools:
$ sudo apt-get update
$ sudo apt-get install -y fio sysbench
Why not dd
Do not compare an unspecified dd throughput figure directly with random-I/O or synchronous-write results. These are different workloads; neither has a universal multiplier that converts it into application performance.
A simple zero-filled dd write has a narrow interpretation: buffering, queue depth, compression and storage caching can all influence the result. Direct I/O does not bypass every host or device cache. fio lets you specify the workload more precisely, but its results still require the same context.
Run the disk tests
Create a folder for the test and change into it:
$ mkdir ~/fio-test
$ cd ~/fio-test
Create ~/fio-test/disk.fio. It describes six tests that run one after another:
[global]
filename=fio-test.dat
size=4G
ioengine=libaio
direct=1
runtime=60
time_based=1
group_reporting=1
stonewall
[random-read-4k]
rw=randread
bs=4k
iodepth=32
[random-write-4k]
rw=randwrite
bs=4k
iodepth=32
[read-latency-4k]
rw=randread
bs=4k
iodepth=1
[sync-write-latency-4k]
rw=randwrite
bs=4k
iodepth=1
fdatasync=1
[sequential-read-1m]
rw=read
bs=1M
iodepth=8
[sequential-write-1m]
rw=write
bs=1M
iodepth=8
The [global] settings apply to every test:
direct=1requests direct I/O, bypassing the guest filesystem page cache for the test file where supported. Hypervisor, controller and device caches may still affect results. It does not isolate physical-disk speed.size=4Gsets the test file size. fio creates the file once before the first test.runtime=60withtime_based=1runs each test for a full minute, long enough to smooth out short bursts.ioengine=libaioandiodepthcontrol how many requests are in flight at once.stonewallmakes each test wait for the previous one to finish, so they never compete.
The six tests cover the patterns that matter:
- random-read-4k and random-write-4k measure IOPS, the number of small operations per second, with 32 requests in flight. This is what databases and busy websites do.
- read-latency-4k sends one read at a time and shows how long a single request takes.
- sync-write-latency-4k writes one block at a time and waits until the disk confirms it is stored, which is what a database does on every commit.
- sequential-read-1m and sequential-write-1m measure throughput with large blocks, as when you copy backups or large files.
Run the tests and save the results. It takes about six minutes:
$ fio disk.fio --output=results.txt
Read the disk results
Print the headline number of each test:
$ grep -E "^[a-z0-9-]+: \(groupid|IOPS=" results.txt
Use the values from your saved result, not an example provider score. Require err=0 for each job and inspect any I/O errors before comparing performance.
How to read each one:
- Random tests: the
IOPSvalue is the number to compare.kmeans thousands. - Latency tests: read the latency and sync-latency fields and their units directly. A reciprocal IOPS calculation for one outstanding operation is only an average approximation; it cannot describe tail latency or prove a storage technology.
- Sequential tests: the
BWvalue, in MiB/s, is the number to compare.
For more detail, open results.txt. Each test has a clat percentiles table; the 99.00th line shows the time within which 99% of requests finished. A low average with a high 99th percentile means occasional slow requests, which users notice as stutters. In the sync-write test, the sync percentiles table shows how long the disk took to confirm each write.
Also check the cpu line under each random test. If usr and sys add up to nearly 100%, fio itself used a full CPU core, and the disk may be faster than the result shows. Run that test again with two jobs, for example:
$ fio --name=random-read-4k --filename=fio-test.dat --size=4G --ioengine=libaio --direct=1 --rw=randread --bs=4k --iodepth=32 --numjobs=2 --runtime=60 --time_based --group_reporting
When you are done, delete the 4 GB test file:
$ rm ~/fio-test/fio-test.dat
Test the CPU
sysbench’s CPU test calculates prime numbers and reports how many rounds it completes per second. Run it once with a single thread and once with all CPU cores:
$ sysbench cpu --threads=1 --time=30 run
$ sysbench cpu --threads=$(nproc) --time=30 run
Compare the events per second values for the same sysbench version and settings. Multiple threads need not scale linearly: CPU frequency, SMT, scheduling, quotas and contention all matter. Record the vCPU allocation and load instead of inferring a provider fault from non-linear scaling.
On a VPS, also check for steal time, the share of time the hypervisor gave your virtual CPU to another machine. Start a CPU load in the background and watch it:
$ sysbench cpu --threads=$(nproc) --time=20 run > /dev/null &
$ vmstat 5 3
$ wait
Ignore the first vmstat data row, which summarizes activity since boot. Later st values can indicate time a virtual CPU waited while the hypervisor scheduled other work. Persistent steal is useful evidence, not proof by itself of an overloaded host; zero steal is not a guarantee of a plan entitlement or fast application. The wait command waits for the background load to finish.
Test memory speed
The following sysbench memory workload repeatedly accesses a 1 MiB buffer. That can fit in CPU cache, so the result is not necessarily main-memory bandwidth. Record block size, threads and access pattern, and use a workload with a working set larger than cache when DRAM behavior is the question.
$ sysbench memory --memory-block-size=1M --memory-total-size=50G run
$ sysbench memory --memory-block-size=1M --memory-total-size=50G --memory-oper=read run
The MiB/sec figure on the transferred line is the result. The first command measures writes and the second reads. Add --threads=$(nproc) to see the combined bandwidth of all cores.
Compare results fairly
A single run is a sample, not a verdict. To compare servers fairly:
- Repeat tests at different times and record the range, concurrent load and any outliers. There is no universal percentage threshold that makes every workload consistent.
- Use the same commands, file size and operating system on every server.
- Record steal time with the results, so you can tell a slow server from a busy host.
- Compare against what you need. A blog needs little; a database server needs low sync-write latency and high random IOPS.
When you choose a plan, run the same set on each candidate, such as an InterServer VPS next to your current server, so you compare like with like.
Troubleshooting
error=No space left on devicebefore the first test: the disk does not have room for the test file. Lowersizeindisk.fio, and delete the partially createdfio-test.dat, which can be very large.fio: engine ... not loadable: theioenginevalue is misspelled. Check it against the file above;libaiois included in Ubuntu’s fio package.- Unexpectedly high results: verify the workload and units, direct-I/O setting, working-set size and possible guest/host/storage caches. A fast number alone does not identify which cache or device produced it.
- Results vary a lot between runs: check steal time and other processes with
topduring the test. Run the tests again at a quieter time before drawing conclusions.
Keep the commands, tool versions, results and workload context as a baseline. A later difference is a reason to investigate caching, contention, configuration and application behavior, not proof that the hardware changed.
Reference: fio workload and result documentation. Check the documentation for your installed version before applying changes.
Reference: sysbench workload documentation. Check the documentation for your installed version before applying changes.
Related guides
Plan a Safe Redis-to-Valkey Migration
Plan a Redis-to-Valkey migration: check version compatibility, rehearse recovery, stop writers for cutover and protect against lost writes during rollback.
Web HostingHow to Self-Host Online Booking with EasyAppointments
Self-host EasyAppointments, protect initial setup and configure booking. Verify scheduling, customer notifications, rescheduling and cancellation.
Web HostingHow to Set Up Meilisearch with Docker
Run Meilisearch with Docker, index sample documents and create restricted search keys. Verify asynchronous tasks, persistence and access boundaries.
Web HostingHow to Collect Nginx Logs with Vector
Collect Nginx logs with Vector, parse structured fields and verify fresh events. Understand checkpoints, rotation and delivery limitations.
