agentskills.codes
K6

k6-stress-testing

Use after `k6-performance-testing` (average-load) establishes a baseline. Covers: stress tests, spike tests, and breaking-point (breakpoint) tests.

Install

mkdir -p .claude/skills/k6-stress-testing && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14945" && unzip -o skill.zip -d .claude/skills/k6-stress-testing && rm skill.zip

Installs to .claude/skills/k6-stress-testing

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use after `k6-performance-testing` (average-load) establishes a baseline. Covers: stress tests, spike tests, and breaking-point (breakpoint) tests.
147 chars · catalog descriptionno explicit “when” trigger

About this skill

Skill: k6 Stress Testing

When to Use

Invoke this skill when the task is to validate system behaviour beyond normal capacity — to observe how it degrades and whether it recovers. This skill answers: "What happens when load exceeds the normal operating point?"

Use after k6-performance-testing (average-load) establishes a baseline. Covers: stress tests, spike tests, and breaking-point (breakpoint) tests.

Test Types Covered

TypeTarget VU/RateDurationGoal
Stress≥ 120–200% of normal45–60 minObserve degradation and recovery behaviour
SpikeExtreme burst (2000+ VUs), no plateau3–5 minValidate sudden traffic surge handling
BreakpointContinuously ramping until failureUntil abortFind the system capacity ceiling

Executor Choice: Open vs Closed Model

Always use ramping-arrival-rate (open model) for stress and breakpoint tests.

ModelExecutorsBehaviour under stress
Closedramping-vus, constant-vusIteration rate drops as system slows → masks true failure rate (coordinated omission)
Openramping-arrival-rate, constant-arrival-rateIteration starts are decoupled from completion → accurately reflects real-world arrival pressure

With VU-based executors, as the system slows down, fewer iterations complete per second — the load generator backs off invisibly. The system appears healthy while it is actually overloaded.

Stress Test Script

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';

const appErrorRate = new Rate('app_errors');

export const options = {
  scenarios: {
    stress: {
      executor: 'ramping-arrival-rate',
      startRate: 10,
      timeUnit: '1s',
      preAllocatedVUs: 100,       // allocate generously — avoids spawn latency
      maxVUs: 500,
      stages: [
        { duration: '2m',  target: 10  }, // warm-up at baseline
        { duration: '10m', target: 100 }, // ramp to stress level (100 req/s)
        { duration: '20m', target: 100 }, // hold at stress load
        { duration: '5m',  target: 10  }, // ramp down — observe recovery
      ],
      tags: { phase: 'stress' },
    },
    spike: {
      executor: 'ramping-arrival-rate',
      startTime: '37m',            // starts after stress phase completes
      startRate: 10,
      timeUnit: '1s',
      preAllocatedVUs: 200,
      maxVUs: 800,
      stages: [
        { duration: '30s', target: 10  }, // brief baseline
        { duration: '2m',  target: 500 }, // sudden spike to 500 req/s
        { duration: '30s', target: 10  }, // quick return to baseline
      ],
      tags: { phase: 'spike' },
    },
  },
  thresholds: {
    // Degraded SLO — test FAILS at end but does not abort
    'http_req_duration{phase:stress}': ['p(95)<800'],
    'http_req_duration{phase:spike}':  ['p(95)<1500'], // looser during spike

    // Broken threshold — abort test if error rate exceeds 5%
    http_req_failed: [{
      threshold: 'rate<0.05',
      abortOnFail: true,
      delayAbortEval: '30s',  // ignore transient spikes during ramp-up
    }],

    // Application-level error guard
    app_errors: [{
      threshold: 'rate<0.05',
      abortOnFail: true,
      delayAbortEval: '30s',
    }],
  },
};

export default function () {
  const res = http.get(`${__ENV.BASE_URL}/api/health`, { timeout: '10s' });

  const ok = check(res, {
    'status is 200':       (r) => r.status === 200,
    'response time < 2s':  (r) => r.timings.duration < 2000,
    'no error in body':    (r) => !r.body.includes('error'),
  });

  if (!ok) {
    appErrorRate.add(1);
    return; // skip dependent steps gracefully
  }

  appErrorRate.add(0);
  sleep(1); // ALWAYS include think time
}

Breakpoint Test Script

export const options = {
  scenarios: {
    breakpoint: {
      executor: 'ramping-arrival-rate',
      startRate: 10,
      timeUnit: '1s',
      preAllocatedVUs: 200,
      maxVUs: 5000,
      stages: [
        { duration: '2h', target: 20000 }, // ramp until the system breaks
      ],
    },
  },
  thresholds: {
    http_req_failed: [{
      threshold: 'rate<0.05',
      abortOnFail: true,
      delayAbortEval: '60s',  // longer delay for gradual ramp
    }],
    http_req_duration: [{
      threshold: 'p(99)<5000',
      abortOnFail: true,
      delayAbortEval: '60s',
    }],
  },
};

Stage Configuration Reference

Standard stress stages (ramp-up → hold → ramp-down):

stages: [
  { duration: '10m', target: 200 }, // ramp from 0 to 200 over 10 minutes
  { duration: '30m', target: 200 }, // hold at peak stress load
  { duration: '5m',  target: 0   }, // ramp down
],

Zero-duration stage for instantaneous spike:

stages: [
  { target: 200, duration: '30s' }, // gradual ramp to 200 req/s
  { target: 500, duration: '0'   }, // instant jump to 500 req/s
  { target: 500, duration: '10m' }, // hold
],

Detecting the Breaking Point

Watch for these signals in k6 output and logs:

SignalMetricBreaking-Point Indicator
Error rate cliffhttp_req_failedJumps from < 0.5% to > 5–10% in a short window
Latency cliffhttp_req_duration p(95)/p(99)Doubles or triples non-linearly from baseline
Queue builduphttp_req_waitingRising steadily → sudden jump (queue overflow)
Connection exhaustionhttp_req_blockedRising rapidly — TCP connection pool exhausted
VU starvationdropped_iterationsNon-zero counter (arrival-rate: pool exhausted)
Connection resetk6 log outputconnection reset by peer / i/o timeout errors

Threshold Patterns for Stress

thresholds: {
  // Degraded — warn at test end, do not abort
  http_req_duration: ['p(95)<500'],

  // Broken — abort immediately (with delay for transient spikes)
  http_req_failed: [{
    threshold: 'rate<0.05',
    abortOnFail: true,
    delayAbortEval: '30s',
  }],

  // Extreme latency guard
  http_req_duration: [{
    threshold: 'p(99)<2000',
    abortOnFail: true,
    delayAbortEval: '30s',
  }],
}

Note: You cannot declare the same metric key twice — use an array for multiple expressions.

ramping-arrival-rate Parameter Reference

{
  executor: 'ramping-arrival-rate',
  startRate: 300,         // iterations per timeUnit at test start
  timeUnit: '1m',         // default '1s'
  preAllocatedVUs: 50,    // VUs allocated before test starts (avoids spawn latency)
  maxVUs: 200,            // optional cap on spawned VUs
  stages: [
    { target: 300, duration: '1m' },
    { target: 600, duration: '2m' },
    { target: 60,  duration: '2m' },
  ],
}

Critical: preAllocatedVUs must be large enough that k6 does not spawn VUs mid-test. Monitor dropped_iterations — a non-zero counter means the system is saturated.

CLI Usage

# Run with env var
k6 run --env BASE_URL=https://staging.api.example.com stress.js

# Tag the test run for analysis
k6 run --tag test-type=stress --tag env=staging stress.js

# Pass stage config via CLI (useful for CI parameterization)
k6 run --stage 5s:10,5m:200,10s:5 script.js
# or
K6_STAGES="10m:200,30m:200,5m:0" k6 run script.js

# Exploratory run (skip threshold evaluation)
k6 run --no-thresholds stress.js

# Export raw metrics for post-analysis
k6 run --out json=results/stress-output.json stress.js

Anti-Patterns to Avoid

Anti-patternProblemFix
No sleep() in VU bodyVU runs at full CPU speed; load is unrealistic and overloads k6 hostAdd sleep(1) or sleep(Math.random() * 3 + 1)
Using ramping-vus for breakpointClosed model backs off as system slows — masks the true failure rateUse ramping-arrival-rate
preAllocatedVUs too smallDynamic VU spawn mid-test or silently dropped iterationsSize to handle peak iteration rate; monitor dropped_iterations
No abortOnFail on breakpointTest runs for hours past the breaking pointSet abortOnFail: true + delayAbortEval: '60s'
abortOnFail with no delayAbortEvalTransient cold-start errors abort the test prematurelyAlways set delayAbortEval to ≥ 20s for stress, ≥ 60s for breakpoint
Confusing VU count with RPSWith sleep(1), 100 VUs ≈ 100 RPS; without, 100 VUs >> 100 RPSUse ramping-arrival-rate with explicit rate for precise RPS control
Not setting gracefulRampDownVUs killed during ramp-down produce spurious interrupted errorsSet gracefulRampDown: '30s' (default) or higher
Running from a single weak machinek6 host becomes the bottleneck before the SUTUse --execution-segment for distributed testing or Grafana Cloud k6

Sources

Search skills

Search the agent skills registry