Skip to main content
Thresholds are pass/fail criteria for your load tests. They specify the acceptable limits for your metrics, allowing you to automate test evaluation.

What are Thresholds?

From metrics/thresholds.go, thresholds are expressions that evaluate metric values against defined criteria. If a threshold fails, k6 exits with a non-zero status code.
import http from 'k6/http';

export const options = {
  thresholds: {
    // 95th percentile response time must be below 500ms
    http_req_duration: ['p(95)<500'],
    
    // Less than 1% of requests can fail
    http_req_failed: ['rate<0.01'],
  },
};

export default function() {
  http.get('https://quickpizza.grafana.com/');
}
When thresholds fail, k6 exits with code 99 (from errext/exitcodes).

Threshold Syntax

From metrics/thresholds_parser.go, threshold expressions follow this format:
aggregationMethod(value) operator value

Supported Operators

From metrics/thresholds.go, the run() function supports:
  • > - Greater than
  • >= - Greater than or equal
  • < - Less than
  • <= - Less than or equal
  • == or === - Equal to
  • != - Not equal to

Examples

export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500'],        // 95th percentile < 500ms
    http_req_duration: ['avg<200'],          // Average < 200ms
    http_req_duration: ['max<2000'],         // Maximum < 2s
    http_req_duration: ['med<150'],          // Median < 150ms
    http_req_failed: ['rate<0.01'],          // Error rate < 1%
    http_reqs: ['count>100'],                // At least 100 requests
    checks: ['rate>0.95'],                   // 95% of checks pass
  },
};

Aggregation Methods by Metric Type

From metrics/metric_type.go, each metric type supports specific aggregation methods:

Counter Metrics

Supported methods: count, rate
export const options = {
  thresholds: {
    http_reqs: ['count>1000'],               // Total requests > 1000
    http_reqs: ['rate>50'],                  // Request rate > 50/sec
    iterations: ['count>=500'],              // At least 500 iterations
  },
};

Gauge Metrics

Supported methods: value
export const options = {
  thresholds: {
    vus: ['value>10'],                       // VUs > 10
    vus_max: ['value<=100'],                 // Max VUs ≤ 100
  },
};

Trend Metrics

Supported methods: avg, min, max, med, p(N)
export const options = {
  thresholds: {
    http_req_duration: [
      'avg<200',                             // Average < 200ms
      'min<50',                              // Minimum < 50ms  
      'max<1000',                            // Maximum < 1s
      'med<150',                             // Median < 150ms
      'p(90)<400',                           // 90th percentile < 400ms
      'p(95)<500',                           // 95th percentile < 500ms
      'p(99)<800',                           // 99th percentile < 800ms
      'p(99.9)<1000',                        // 99.9th percentile < 1s
    ],
    iteration_duration: ['p(95)<5000'],      // 95% complete in 5s
  },
};

Rate Metrics

Supported methods: rate
export const options = {
  thresholds: {
    checks: ['rate>0.95'],                   // 95% success rate
    http_req_failed: ['rate<0.01'],          // < 1% failures
  },
};

Multiple Thresholds

Define multiple thresholds for the same metric from examples/thresholds.js:
export const options = {
  thresholds: {
    http_req_duration: [
      'p(95)<500',    // First threshold
      'p(99)<1000',   // Second threshold
      'max<2000',     // Third threshold
    ],
  },
};
All thresholds must pass for the test to succeed.

Thresholds on Tagged Metrics

From examples/thresholds.js, filter metrics by tags:
export const options = {
  thresholds: {
    // Threshold for all requests
    'http_req_duration': ['p(95)<500'],
    
    // Threshold for specific URL
    'http_req_duration{name:http://httpbin.org/post}': ['max<1000'],
    
    // Threshold for custom tag
    'http_req_duration{staticAsset:yes}': ['p(99)<500'],
  },
};

export default function() {
  http.get('https://quickpizza.grafana.com/');
  http.post('https://quickpizza.grafana.com/post', {data: 'some data'});
  
  http.get('https://quickpizza.grafana.com/style.css', {
    tags: { staticAsset: 'yes' },
  });
}

Aborting on Threshold Failure

From metrics/thresholds.go, use abortOnFail to stop tests immediately when a threshold fails:
export const options = {
  thresholds: {
    http_req_failed: [
      // Normal threshold
      'rate<0.01',
      
      // Abort if error rate exceeds 5%
      { threshold: 'rate<=0.05', abortOnFail: true },
    ],
  },
};
From the source code:
  • When abortOnFail is true and the threshold fails, ts.Abort is set
  • The test terminates early
  • Useful for preventing wasted test time when critical issues occur

Abort Grace Period

From metrics/thresholds.go, delay abort to allow test startup:
export const options = {
  thresholds: {
    http_req_failed: [
      {
        threshold: 'rate<=0.05',
        abortOnFail: true,
        delayAbortEval: '1m',  // Don't abort for first minute
      },
    ],
  },
};
Grace periods prevent false positives during test ramp-up when metrics may be unstable.

Real-World Example

From examples/thresholds_readme_example.js:
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Rate } from 'k6/metrics';

// Custom metric to track failure rates
const failureRate = new Rate('check_failure_rate');

export const options = {
  stages: [
    { target: 50, duration: '1m' },
    { target: 50, duration: '3m30s' },
    { target: 0, duration: '30s' },
  ],
  thresholds: {
    // 95th percentile of all requests < 750ms
    'http_req_duration': ['p(95)<750'],
    
    // Static assets finish faster
    'http_req_duration{staticAsset:yes}': ['p(99)<500'],
    
    // Custom metric thresholds
    'check_failure_rate': [
      // Global failure rate < 1%
      'rate<0.01',
      
      // Abort if it exceeds 5%
      { threshold: 'rate<=0.05', abortOnFail: true },
    ],
  },
};

export default function () {
  const response = http.get('https://quickpizza.grafana.com/test.k6.io/');

  const checkRes = check(response, {
    'http2 is used': (r) => r.proto === 'HTTP/2.0',
    'status is 200': (r) => r.status === 200,
    'content is present': (r) => r.body.indexOf('This is a replacement') !== -1,
  });

  // Track failures
  failureRate.add(!checkRes);

  // Load static assets
  group('Static Assets', function () {
    const resps = http.batch([
      ['GET', 'https://quickpizza.grafana.com/test.k6.io/static/css/site.css', 
       null, { tags: { staticAsset: 'yes' } }],
      ['GET', 'https://quickpizza.grafana.com/test.k6.io/static/favicon.ico', 
       null, { tags: { staticAsset: 'yes' } }],
    ]);
    
    failureRate.add(!check(resps, {
      'status is 200': (r) => r[0].status === 200 && r[1].status === 200,
    }));
  });

  sleep(Math.random() * 3 + 2);
}

Threshold Validation

From metrics/thresholds.go, the Validate() function ensures:
  1. The metric exists in the registry
  2. The aggregation method is supported for the metric type
  3. The threshold expression is syntactically correct
Invalid thresholds cause k6 to exit with exitcodes.InvalidConfig.

Common Validation Errors

export const options = {
  thresholds: {
    // ❌ ERROR: 'avg' not supported for Counter
    http_reqs: ['avg<100'],
    
    // ❌ ERROR: 'count' not supported for Rate  
    checks: ['count>50'],
    
    // ✅ CORRECT: 'rate' supported for Rate
    checks: ['rate>0.95'],
    
    // ✅ CORRECT: 'count' supported for Counter
    http_reqs: ['count>100'],
  },
};

Threshold Execution

From metrics/thresholds.go, the Run() method:
  1. Collects sink values (aggregated metrics)
  2. Evaluates each threshold expression
  3. Sets LastFailed flag on failing thresholds
  4. Determines if test should abort

Sink Values

From metrics/thresholds.go, different sinks provide different values: CounterSink:
  • count - Total value
  • rate - Value per second
GaugeSink:
  • value - Current value
TrendSink:
  • min, max, avg, med - Statistics
  • p(N) - Percentiles (calculated on demand)
RateSink:
  • rate - Ratio of true values to total

Thresholds in CI/CD

Thresholds make k6 tests suitable for automated pipelines:
# Run test
k6 run test.js

# Check exit code
if [ $? -eq 0 ]; then
  echo "Test passed!"
else
  echo "Test failed - thresholds not met"
  exit 1
fi

Exit Codes

  • 0 - All thresholds passed
  • 99 - One or more thresholds failed (from errext/exitcodes)
  • Other codes indicate different errors

Best Practices

1

Start Conservative

Begin with loose thresholds and tighten them as you understand system behavior.
2

Focus on Percentiles

Use p(95) or p(99) instead of averages to catch outliers.
3

Set Multiple Thresholds

Define thresholds for different aspects: latency, error rate, throughput.
4

Use Tags for Granularity

Apply different thresholds to different request types or endpoints.
5

Add Abort Conditions

Use abortOnFail for critical failures to save resources.
Too strict thresholds cause false failures. Base thresholds on realistic performance expectations.
Combine thresholds with custom metrics to track business-specific SLOs (Service Level Objectives).

Common Threshold Patterns

API Performance SLO

export const options = {
  thresholds: {
    http_req_duration: ['p(99)<1000', 'p(95)<500'],
    http_req_failed: ['rate<0.001'],  // 99.9% success
    http_reqs: ['rate>10'],           // Minimum throughput
  },
};

Multi-Environment Thresholds

const thresholds = __ENV.ENV === 'prod' 
  ? { http_req_duration: ['p(95)<200'] }      // Strict for production
  : { http_req_duration: ['p(95)<1000'] };    // Relaxed for staging

export const options = { thresholds };

Progressive Thresholds

export const options = {
  thresholds: {
    // Must meet all of these
    http_req_duration: [
      'p(50)<100',   // Half of requests < 100ms
      'p(90)<300',   // 90% < 300ms
      'p(95)<500',   // 95% < 500ms
      'p(99)<1000',  // 99% < 1s
    ],
  },
};
Thresholds transform k6 from a measurement tool into an automated testing framework, enabling continuous performance validation in your development pipeline.

Build docs developers (and LLMs) love