Authors k6 JavaScript load-test scripts (VU loops + checks + sleeps), configures the `options` block with `stages` (ramp-up patterns) and `thresholds` (p(95) latency, error rate), runs via `k6 run script.js` or `--vus / --duration` ad-hoc flags, and uses thresholds as the CI pass/fail signal. Use when the project ships HTTP / WebSocket / gRPC load tests and the team wants developer-friendly JavaScript authoring.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
k6 tests are .js files with a default-exported function that runs
once per virtual user (VU) per iteration (per k6-running).
This skill covers the load-testing workflow; k6's browser, synthetic
monitoring, and chaos modes share the same script structure but are
out of scope here.
If the team is already deep in JMeter, the migration cost is non-
trivial - evaluate jmeter-load-testing
in place. For Python / Locust shops, see
locust-load-testing. For JVM /
Gatling, see gatling-load-testing.
Install via k6 installation (brew, apt/yum, choco, Docker). Pin a specific k6 version in CI rather than "latest".
import http from 'k6/http';
import { check, sleep } from 'k6';
export default function () {
const res = http.get('https://quickpizza.grafana.com/');
check(res, { 'status was 200': (r) => r.status == 200 });
sleep(1);
}(Per k6-running.)
The default-exported function runs once per virtual-user iteration.
check() is a non-failing assertion that contributes to the
checks metric; sleep() simulates think-time between requests.
Per k6-running, the options export controls the run:
export const options = {
stages: [
{ duration: '30s', target: 20 }, // ramp up to 20 VUs over 30s
{ duration: '1m30s', target: 10 }, // hold ~10 VUs for 90s (gradual scale-down)
{ duration: '20s', target: 0 }, // ramp down to 0
],
};Three canonical shape patterns:
| Pattern | Stages |
|---|---|
| Smoke test | One stage, { duration: '1m', target: 1 } - sanity check. |
| Average load test | Ramp up → plateau → ramp down (the example above). |
| Stress test | Ramp past expected peak; observe where the system breaks. |
| Spike test | Sudden ramp to high VU; back to normal; verify recovery. |
| Soak test | Long plateau (hours); verify no resource leaks over time. |
Per k6-thresholds, thresholds are "the pass/fail criteria that you define for your test metrics" - the canonical CI gate:
export const options = {
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // <1% errors
checks: ['rate>0.99'], // >99% of checks pass
},
};(Per k6-thresholds.)
The threshold expression syntax is <aggregation_method> <operator> <value>. Available aggregation methods per metric type:
| Metric type | Methods |
|---|---|
| Trend | avg, min, max, med, p(N) (percentile, ms) |
| Counter | count, rate |
| Rate | rate |
| Gauge | value |
A test that fails any threshold exits non-zero - the canonical CI gate signal.
abortOnFailFor long-running stress / soak tests, abort early if a threshold is already violated (k6-thresholds):
thresholds: {
http_req_duration: [
{ threshold: 'p(95)<500', abortOnFail: true, delayAbortEval: '10s' },
],
},delayAbortEval postpones the abort decision to let metrics
accumulate before evaluating; without it, a 1-second test could
abort on a single slow request.
k6 run --vus 10 --duration 30s script.js(Per k6-running.)
--vus overrides the script's options; --duration runs for a
fixed time without ramps.
k6 run script.jsoptions block in the script controls everything; the CLI is
hands-off. Preferred for CI.
| Flag | Purpose |
|---|---|
--out json=<file> | Stream raw metrics to a JSON file. |
--summary-export=<file> | Write the end-of-test summary to JSON. |
--quiet | Silence the live progress UI (CI noise). |
--http-debug=full | Verbose HTTP debug for triage runs. |
--env KEY=VAL | Inject env vars accessible via __ENV.KEY. |
--config <file> | Externalize the options block to a JSON file. |
The end-of-run summary prints to stdout; --summary-export writes
the same data as JSON for downstream consumption:
{
"metrics": {
"http_req_duration": {
"values": { "p(95)": 432.1, "avg": 198.3, ... },
"thresholds": { "p(95)<500": { "ok": true } }
},
"http_req_failed": {
"values": { "rate": 0.004, "passes": 996, "fails": 4 },
"thresholds": { "rate<0.01": { "ok": true } }
}
}
}Pipe to jq for a quick gate report:
jq -r '
.metrics
| to_entries[]
| select(.value.thresholds)
| .key + ": " + (.value.thresholds | to_entries | map("\(.key) → \(if .value.ok then "PASS" else "FAIL" end)") | join(", "))
' summary.jsonFull GitHub Actions workflow - apt install, PR + nightly triggers,
summary upload via if: always() - in
references/ci-integration.md. A
failing threshold causes k6 run to exit non-zero, failing the job.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Hard-coded URLs / tokens in script | Scripts bind to one environment; secrets leak. | Read from __ENV.API_BASE_URL, __ENV.API_TOKEN. |
--vus 1000 --duration 1h from a developer laptop | Laptop resource contention; client-side bottlenecks corrupt metrics. | Run from a CI runner / dedicated load generator; never from a dev box for serious numbers. |
Threshold p(95)<10000 | Practically meaningless - passes any sane API. Hides regressions. | Set thresholds at meaningful budgets (500ms, 1s) tied to NFRs from the non-functional-requirement-extractor. |
| Soak tests in PR CI | Multi-hour PR CI; team disables. | Soak tests are scheduled-only; PRs run smoke / average load. |
Missing sleep() between requests | Hammering at full VU rate generates synthetic numbers; doesn't model real users. | Include sleep(1) or randomized think-time after each iteration. |
Asserting only http_req_failed rate | A 30-second response that succeeds passes the rate gate but breaks UX. | Always pair with http_req_duration percentile thresholds. |
k6/browser module covers it
but adds Chromium overhead; for browser-driven perf testing,
consider lighthouse-perf instead.k6 archive to bundle.jmeter-load-testing,
gatling-load-testing,
locust-load-testing -
alternatives by language stack.perf-budget-gate - downstream
gate that aggregates k6 / lighthouse / load-runner verdicts.