Getting started¶
Install¶
$ pip install k9-load # or: uv tool install k9-load, pipx install k9-load
$ k9 version
Wheels cover Linux (x86_64, aarch64), macOS (arm64, x86_64) and Windows
(x86_64), for Python 3.10 or newer; no Rust needed. Each GitHub release also
has a standalone k9 binary that runs declarative plans without Python.
To build from source (Rust 1.99 or newer, a C toolchain and uv), see Development.
Your first test¶
k9 new writes a starter that runs as is (see Writing tests).
Point it at a service you run, never at one you don't own:
$ k9 new --url http://localhost:3000/health # script.py: async def default()
$ k9 run script.py -u 5 -d 10s
Without --url the starter targets http://localhost:8080/ok, the test server
in this repository (cargo run -p k9-testserver).
Plans, checks and thresholds¶
Declarative plans use k6's option names. See examples/basic.yaml.
$ cargo run -p k9-testserver & # a local target on :8080
$ k9 run examples/local.yaml # run a test, print the summary
$ k9 inspect examples/basic.yaml # print the fully resolved plan as JSON
$ k9 run plan.yaml -u 20 -d 1m # override the plan's load shape, as in k6
$ k9 run plan.yaml -s 30s:10 -s 1m:10 -s 15s:0 --insecure-skip-tls-verify
For CI, add checks and thresholds (see examples/ci.yaml).
A crossed threshold exits with code 99, as in k6:
█ api
✗ api ok
↳ 90% — ✓ 270 / ✗ 30
✗ checks.........................: 95.00% ✓ 570 ✗ 30
✓ http_req_duration..............: avg=51.12ms min=203.69µs med=1.14ms max=102.78ms p(90)=102.24ms p(95)=102.37ms
✓ { group:::api }..............: avg=471.57µs min=203.69µs med=442µs max=1.14ms p(90)=707µs p(95)=797µs
✗ http_req_failed................: 5.00% ✓ 30 ✗ 570
ERROR thresholds on metrics 'http_req_failed, checks' have been crossed
Overriding options¶
Options resolve like k6's: flags (-u/--vus, -d/--duration, -i/--iterations,
-s/--stage, --max-redirects, --insecure-skip-tls-verify, --user-agent,
--discard-response-bodies) beat K9_* environment variables (K9_VUS,
K9_DURATION, K9_STAGES=30s:10,1m:0, …), which beat the plan.
running (00m10.1s), 00/10 VUs, 990 complete and 0 interrupted iterations
data_received..................: 129 kB 13 kB/s
data_sent......................: 70 kB 7.0 kB/s
http_req_duration..............: avg=101.88ms min=100.77ms med=101.79ms max=103.13ms p(90)=102.5ms p(95)=102.62ms
{ expected_response:true }...: avg=101.88ms min=100.77ms med=101.79ms max=103.13ms p(90)=102.5ms p(95)=102.62ms
http_req_failed................: 0.00% ✓ 0 ✗ 990
http_reqs......................: 990 98.100557/s
iteration_duration.............: avg=101.9ms min=100.78ms med=101.86ms max=103.13ms p(90)=102.5ms p(95)=102.62ms
iterations.....................: 990 98.100557/s
vus............................: 10 min=10 max=10
vus_max........................: 10 min=10 max=10
When a plan is wrong¶
Invalid plans list every problem and exit with code 104, as k6 does:
error: bad.yaml: invalid plan (2 problems):
- options.scenarios.fast.rate: not valid for constant-vus (valid: `vus`, `duration`)
- options.scenarios.fast.duration: required for constant-vus