TL;DR

The 8 most useful fio invocations. Copy-pasteable. If you remember nothing else, remember the first one.

The essentials

# Sequential read of a 1 GiB file (writes nothing; pre-existing file needed)
$ fio --name=read --rw=read --size=1G --filename=/tmp/test.bin
Random 4 KiB write benchmark (4 parallel jobs, 1 GiB each)
$ fio --rw=randwrite --bs=4k --size=1G --numjobs=4 \
      --filename=/tmp/fio-test.bin --runtime=60 --time_based

IO engines

Linux io_uring engine (kernel ≥ 5.6; lowest-latency async path)
$ fio --ioengine=io_uring --rw=randrw --bs=4k \
      --size=1G --filename=/dev/nvme0n1 --runtime=30 --time_based
Sanity check the binary without touching disk (null engine; useful in CI)
$ fio --name=smoke --ioengine=null --rw=randrw \
      --runtime=1 --time_based --size=1M --minimal
3;fio-3.41;smoke;0;0;17175736;17158577;4289644;1001;...

Output & reporting

Latency histogram + percentile breakdown (stdout, terse format)
$ fio --rw=randread --bs=4k --size=1G --filename=/dev/nvme0n1 \
      --runtime=30 --time_based --output-format=normal,latency
JSON output for downstream parsing (e.g. dashboards, regression tests)
$ fio --rw=randwrite --bs=4k --size=1G --filename=/tmp/test.bin \
      --output-format=json --output=out.json
$ jq '.jobs[0].write.iops' out.json
52319.4

Advanced

Multi-device striping (split the workload across two block devices)
$ fio --rw=randrw --bs=4k --size=2G \
      --filename=/dev/nvme0n1 --filename=/dev/nvme1n1 \
      --numjobs=2

When NOT to use fio

fio is a benchmark tool — it generates synthetic workloads and reports throughput / latency. If you want a single-shot dd-style sequential write, or a one-line disk speed number, fio is overkill. For those:

And if you need reproducible synthetic workloads at scale (CI / regression), use --minimal --output-format=json and parse the JSON — that's the test-driven fio workflow.