PERFORMANCE
August 5, 2026

How to Benchmark Your Server's Real Performance

6 min read
Author
CloudStick Team
DevOps Engineer
Share this article
How to Benchmark Your Server's Real Performance
CloudStick
Know Your
Real Limits

Synthetic Benchmarks vs Real Load Testing

A synthetic benchmark isolates one subsystem — CPU, memory, disk, or the database engine — and hammers it directly with an artificial workload, producing clean numbers that describe raw hardware capability but say nothing about how your actual application behaves under real users. Load testing does the opposite: it sends HTTP requests at your live application the way real visitors would, walking through Nginx, PHP-FPM, your database queries, and every plugin or middleware in between, so the number it produces is the one that actually predicts what happens on launch day.

You need both, and in a specific order. Synthetic tools tell you whether the box itself is healthy and where its ceiling is — a disk that can only push 300 IOPS is a disk problem no amount of application tuning will fix. Load testing then tells you whether your specific stack configuration reaches that ceiling gracefully or falls over well before it, which is almost always a PHP-FPM pool size, database query, or caching gap rather than a hardware limit.

PREREQUISITE

Run every benchmark in this article against a staging clone, never against a live production box under real traffic. A production server already carrying real requests will show you a blend of your test load and organic traffic with no way to separate the two, and a heavy sysbench or fio run can degrade or crash the site you are trying to protect. Clone the server, or spin up an identical spec, and benchmark there first.

Benchmark CPU and Memory With sysbench

sysbench is the standard tool for isolated CPU and memory throughput numbers on Linux, and it ships as a single binary with no application dependencies, which is exactly why it is the right first test on a fresh box. The CPU test calculates prime numbers up to a limit across a set number of threads; the memory test measures raw read/write throughput to RAM.

apt install sysbench
# CPU: prime calculation across all 4 vCPUs
sysbench cpu --cpu-max-prime=20000 --threads=4 run
# Memory: 10GB of sequential 1K reads/writes
sysbench memory --memory-block-size=1K --memory-total-size=10G run

Read the "events per second" line from the CPU test and the "MiB/sec transferred" figure from the memory test, then compare both against a known-good run on the same plan tier — a big drop signals a noisy neighbor on shared hosting or a throttled vCPU, not something you would ever see in application logs. On a typical CloudStick 4 vCPU / 8GB server this is a five-minute sanity check before you invest time in anything more elaborate.

Benchmark Disk I/O With fio

fio measures IOPS, throughput, and latency on the actual disk your database writes to, and it is the single most important benchmark for any database-heavy site, because a MySQL or MariaDB slowdown that looks like a query problem is very often a disk that cannot sustain enough random write IOPS under concurrent load.

apt install fio
# random 4K writes, 4 jobs, direct I/O bypassing page cache
fio --name=randwrite --directory=/tmp/fiotest --ioengine=libaio \
--rw=randwrite --bs=4k --numjobs=4 --size=1G \
--runtime=60 --direct=1 --group_reporting

Read the "iops" and "clat" (completion latency) numbers in the output, not just throughput in MB/s — a disk can report solid sequential throughput while its random-write IOPS and latency under concurrency are actually terrible, and random writes are exactly what a busy MySQL instance generates. `--direct=1` is essential here since it bypasses the page cache and measures the real disk, not RAM pretending to be disk. Delete the test file afterward with `--rw=randwrite --unlink=1` or a manual `rm` so you don't leave gigabytes of test data behind.

Measure HTTP Throughput and Latency With wrk or ab

wrk and Apache Bench (ab) both send real HTTP requests at your live site, which is the load-testing half of this exercise — the number that tells you what actually happens when visitors show up, as opposed to what your disk or CPU can theoretically do in isolation. wrk is the more modern choice: it is multi-threaded, gives you full latency percentiles by default, and handles high concurrency far better than ab.

# wrk: 4 threads, 100 open connections, 30 second run, with latency stats
wrk -t4 -c100 -d30s --latency https://staging.example.com/
# ab equivalent: 10,000 requests, 100 concurrent
ab -n 10000 -c 100 https://staging.example.com/
# hey (Go-based alternative): same shape as ab, easier flags
hey -n 10000 -c 100 https://staging.example.com/

Run this against a real page that hits PHP and the database, not just a static asset, or you are only benchmarking Nginx. While the test runs, keep the CloudStick dashboard's server graphs open in another tab — CPU, RAM, and disk usage are pulled live from the Zabbix Agent 2 installed on every CloudStick server, so you can watch exactly which resource climbs toward its limit at the same moment request latency starts to degrade, without opening a second SSH session just to run `top`.

Benchmark MariaDB Directly With sysbench and mysqlslap

sysbench also ships an OLTP database benchmark that generates realistic read/write query patterns against a test table, isolating the database engine and its configuration from the rest of the application — the right test when you need to know whether MariaDB itself, not PHP or Nginx, is the bottleneck.

sysbench oltp_read_write --db-driver=mysql --mysql-db=benchdb \
--mysql-user=root --mysql-password=yourpass --tables=8 \
--table-size=1000000 prepare
sysbench oltp_read_write --db-driver=mysql --mysql-db=benchdb \
--mysql-user=root --mysql-password=yourpass --tables=8 \
--table-size=1000000 --threads=8 --time=60 run
# mysqlslap: concurrency stress test against real schema
mysqlslap --user=root --password --concurrency=50 --iterations=3 \
--number-of-queries=1000 --create-schema=benchdb --auto-generate-sql

Run `mysqltuner` alongside these tests, not instead of them — it does not generate load itself, but it reads your live MariaDB configuration and usage history and flags an undersized `innodb_buffer_pool_size`, a query cache that is doing more harm than good, or connection limits that will choke under the concurrency you just tested. Always run `sysbench ... cleanup` afterward to drop the benchmark tables so they don't linger in your staging database.

Read p50/p95/p99 Latency, Not the Average

An average latency number hides exactly the requests you care about most. If 95% of requests return in 80ms but the slowest 5% take 4 seconds, the average might report a comfortable 250ms while one in twenty real visitors sits through a near-timeout — averages get dragged down by the fast majority and never surface that tail.

p50 (median) tells you the typical experience, p95 tells you what your slower-than-usual visitors see, and p99 tells you the worst case that is still common enough to matter — both wrk's `--latency` flag and fio's `clat percentiles` output report these directly, so there is no reason to fall back to a plain average. A healthy site keeps p99 within a small multiple of p50; if p99 is 10x or more above p50, something — a slow query, a cold cache, GC pauses, disk contention — is spiking intermittently and needs its own investigation.

Re-test whenever you change PHP version, PHP-FPM pool settings, install a new plugin, resize the server, or move to a different disk tier, and keep the previous run's numbers so you are comparing against a real baseline rather than a gut feeling. In practice: sysbench and fio tell you what the hardware can do, wrk or ab tell you what your application actually delivers under that hardware, and cross-referencing both against the CloudStick dashboard's live CPU/RAM/disk graphs while the test runs is the fastest way to see exactly which resource gives out first — without ever needing a second terminal window.

Leave a comment
Full Name
Email Address
Message
Contents