Automating Performance Regressions with k6 Thresholds
Learn how to implement k6 Thresholds to automate performance regression testing, enforce SLIs, and integrate performance gates into your CI/CD pipeline.
09 Oct 2026, 11:13 UTC

Preventing Performance Degradation in CI/CD
Manual analysis of performance test logs often misses subtle regressions—small increases in latency that eventually compound into system instability. The primary challenge is transforming a performance test from a reporting tool into a quality gate that can automatically fail a build when Service Level Indicators (SLIs) are breached.
k6 solves this using Thresholds: predefined pass/fail criteria integrated directly into the test script. When a threshold is breached, k6 returns a non-zero exit code, allowing CI/CD pipelines (such as GitHub Actions, GitLab CI, or Jenkins) to stop the deployment process immediately.
Prerequisites
- k6 installed on the local machine or CI runner (v0.40.0 or newer recommended).
- A target environment available for testing.
- Basic familiarity with JavaScript (ES6).
Defining Performance Gates
Thresholds are defined within the options object of a k6 script. You can target global metrics, such as the overall failure rate, or specific metrics using tags to isolate individual endpoints.
Global vs. Scoped Thresholds
Global thresholds monitor the health of the entire test run. Scoped thresholds use tags to ensure a slow administrative endpoint doesn't mask a regression in a critical user-facing API.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
thresholds: {
// Global: 95% of all requests must be below 200ms
http_req_duration: ['p(95) < 200'],
// Global: Error rate must be less than 1%
http_req_failed: ['rate < 0.01'],
// Scoped: Only the 'login' endpoint must be below 500ms
'http_req_duration{name:login}': ['p(99) < 500'],
},
};
export default function () {
// Tagging the request to match the scoped threshold
http.get('https://test-api.example.com/login', { tags: { name: 'login' } });
http.get('https://test-api.example.com/home', { tags: { name: 'home' } });
sleep(1);
}
Running the Test
Execute the script from your terminal. No special permissions are required beyond the ability to run the k6 binary and network access to the target.
k6 run script.js
Analyzing Results and Exit Codes
At the end of the execution, k6 prints a summary report. Look for the Thresholds section to verify the status of your gates.
| Metric | Threshold | Actual Value | Status |
|---|---|---|---|
| http_req_duration | p(95) < 200 | 185.2ms | ✓ pass |
| http_req_failed | rate < 0.01 | 0.05 | ✗ fail |
Verification Check: To confirm your CI/CD pipeline will react correctly, run a test with an impossible threshold (e.g., 'p(95) < 1'). Check the shell exit code immediately after the run:
# Linux/macOS
echo $?
# Windows (PowerShell)
$LASTEXITCODE
A value of 0 indicates success; any non-zero value indicates a threshold failure.
Engineering Trade-offs
Percentiles vs. Averages
Avoid using avg for latency thresholds. Averages smooth out spikes, meaning a small number of users experiencing 10-second load times can be hidden by a majority of users experiencing 10ms load times. Always use percentiles (p90, p95, p99) to capture the "tail latency" that affects the worst-off users.
Memory Overhead
Every threshold requires k6 to track and aggregate data in memory. While a few dozen thresholds are negligible, defining hundreds of highly granular thresholds (e.g., one for every single unique URL in a large site) can increase the memory footprint of the k6 process, potentially leading to Out-Of-Memory (OOM) crashes on small CI runners.
Limitations
- Evaluation Timing: By default, thresholds are evaluated at the end of the test. If a system crashes 10 seconds into a 10-minute test, the threshold will only trigger after the full duration completes.
- Static Values: Thresholds are static. They do not automatically adjust based on the baseline of previous runs; you must manually update the script as your performance targets evolve.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.