k6 Thresholds and Checks: Automating Pass/Fail Criteria

How k6 thresholds turn performance budgets into automated pass/fail criteria for CI, and how they differ from checks.

· By perf-test.com Editorial · AI-assisted
k6thresholdsci-cd

One of k6’s most practical features for CI integration is built-in thresholds — pass/fail criteria evaluated against aggregate metrics, causing k6 itself to exit non-zero if violated, without needing the custom post-processing scripts that tools like JMeter typically require to achieve the same CI-gating behavior.

Checks vs thresholds: different jobs

  • Checks (check(response, { 'status is 200': (r) => r.status === 200 })) validate individual responses, similar to JMeter assertions — recorded as a pass rate metric, but a failed check does not, by itself, fail the overall k6 run.
  • Thresholds are aggregate criteria applied to metrics (including the check pass rate itself, or response time percentiles, or error rate) — a threshold violation does cause k6 to exit non-zero, directly gating CI.

Defining thresholds

export const options = {
  thresholds: {
    http_req_duration: ['p(95)<300', 'p(99)<800'],
    http_req_failed: ['rate<0.01'],
    checks: ['rate>0.99'],
  },
};

This fails the test run if 95th percentile response time exceeds 300ms, 99th percentile exceeds 800ms, the HTTP failure rate exceeds 1%, or fewer than 99% of checks pass — exactly the kind of percentile-based SLO-style criteria this site recommends over average-based criteria (see the article on why percentiles beat averages).

Per-scenario thresholds

Thresholds can be scoped to a specific scenario or tagged subset of requests, not just globally across the whole test — useful when a test exercises multiple distinct flows with different acceptable latency budgets (a search endpoint and a heavier reporting endpoint shouldn’t share the same threshold).

abortOnFail: failing fast

Thresholds support an abortOnFail option that stops the test run immediately on violation rather than running to completion — useful in CI where you want fast feedback and don’t need the full test duration’s worth of data once a threshold has already definitively failed.

Why this matters for CI/CD specifically

Because threshold evaluation and the resulting exit code are native to k6 itself, a CI pipeline step is as simple as running k6 run script.js and checking the exit code — no custom result-file parsing script required, unlike the JMeter pattern of writing a separate script to parse the .jtl file and compare against a stored baseline (covered in this site’s JMeter CI/CD article). This is one of the more concrete, practical advantages of k6’s design for teams building automated performance gates.

Combining with k6’s JSON/summary output for richer reporting

Beyond pass/fail, k6 run --summary-export=summary.json (or the newer handleSummary() function) gives a structured results export for building custom dashboards or trend tracking over time across multiple CI runs — useful once a team wants more than just pass/fail, like tracking whether p95 latency is slowly trending upward over weeks even while staying within threshold.

Takeaway: thresholds are what make k6 genuinely CI-native rather than just CI-compatible — the pass/fail decision is expressed directly in the test script and enforced by k6 itself, without bolted-on tooling.

Discussions coming soon.

Comments are powered by Giscus (GitHub Discussions). Enable them by configuring GISCUS in src/consts.ts — see giscus.app.