k6 Scenarios: Ramp-up, Spike, and Stress Testing
k6's scenarios system gives you fine-grained control over how load is applied to your system. Instead of a single thread count and duration, you can model realistic traffic patterns — gradual ramp-ups, sudden spikes, sustained soak tests — with different executors for each shape.
What Are k6 Scenarios?
Scenarios define how virtual users (VUs) behave over time. Each scenario uses an executor that controls the VU lifecycle. You can run multiple scenarios simultaneously, each targeting different endpoints or using different load patterns.
Basic scenario structure:
export const options = {
scenarios: {
my_scenario: {
executor: 'ramping-vus',
stages: [
{ duration: '30s', target: 10 },
{ duration: '1m', target: 50 },
{ duration: '30s', target: 0 },
],
},
},
};k6 Executors
ramping-vus
The most common executor. Ramps VU count through defined stages.
export const options = {
scenarios: {
ramp_up: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '1m', target: 50 }, // ramp up
{ duration: '3m', target: 50 }, // hold
{ duration: '30s', target: 0 }, // ramp down
],
gracefulRampDown: '30s',
},
},
};Use for: standard load tests where you want a warm-up period before sustained load.
constant-vus
Runs a fixed number of VUs for a fixed duration.
export const options = {
scenarios: {
baseline: {
executor: 'constant-vus',
vus: 20,
duration: '2m',
},
},
};Use for: baseline performance benchmarks where you want consistent, repeatable conditions.
constant-arrival-rate
Maintains a constant request rate regardless of VU count. This is more realistic than VU-based approaches for services where you want X requests/second.
export const options = {
scenarios: {
steady_rate: {
executor: 'constant-arrival-rate',
rate: 100, // 100 iterations per timeUnit
timeUnit: '1s', // per second = 100 RPS
duration: '2m',
preAllocatedVUs: 50, // initial VU pool
maxVUs: 100, // upper bound
},
},
};Use for: API rate testing, SLA validation, measuring behavior at a specific throughput target.
ramping-arrival-rate
Like constant-arrival-rate but ramps the rate through stages.
export const options = {
scenarios: {
ramping_rate: {
executor: 'ramping-arrival-rate',
startRate: 10,
timeUnit: '1s',
preAllocatedVUs: 50,
maxVUs: 200,
stages: [
{ target: 50, duration: '30s' }, // 50 RPS
{ target: 100, duration: '1m' }, // 100 RPS
{ target: 200, duration: '1m' }, // 200 RPS (stress)
{ target: 0, duration: '10s' },
],
},
},
};per-vu-iterations
Each VU runs exactly N iterations.
export const options = {
scenarios: {
fixed_iterations: {
executor: 'per-vu-iterations',
vus: 10,
iterations: 100, // 10 VUs × 100 = 1000 total iterations
maxDuration: '5m',
},
},
};Use for: smoke tests, data seeding, or tests where you need an exact request count.
shared-iterations
All VUs share a pool of iterations — faster VUs pick up more work.
export const options = {
scenarios: {
total_requests: {
executor: 'shared-iterations',
vus: 20,
iterations: 1000,
maxDuration: '5m',
},
},
};Load Test Patterns
Ramp-Up Test
Validates behavior as load grows. Finds the point where performance degrades.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
scenarios: {
ramp_up: {
executor: 'ramping-vus',
stages: [
{ duration: '2m', target: 25 },
{ duration: '5m', target: 100 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 0 },
],
},
},
thresholds: {
http_req_duration: ['p(95)<1000'],
http_req_failed: ['rate<0.05'],
},
};
export default function () {
const res = http.get('https://api.example.com/products');
check(res, { 'status 200': (r) => r.status === 200 });
sleep(1);
}Spike Test
Simulates a sudden surge in traffic — flash sales, viral content, marketing campaigns.
export const options = {
scenarios: {
spike: {
executor: 'ramping-vus',
stages: [
{ duration: '10s', target: 5 }, // baseline
{ duration: '1m', target: 5 }, // hold baseline
{ duration: '10s', target: 500 }, // spike
{ duration: '3m', target: 500 }, // hold spike
{ duration: '10s', target: 5 }, // recover
{ duration: '3m', target: 5 }, // verify recovery
{ duration: '10s', target: 0 },
],
},
},
thresholds: {
http_req_failed: ['rate<0.1'], // allow more errors during spike
http_req_duration: ['p(99)<3000'],
},
};Stress Test
Finds the breaking point by pushing load beyond expected capacity.
export const options = {
scenarios: {
stress: {
executor: 'ramping-arrival-rate',
startRate: 10,
timeUnit: '1s',
preAllocatedVUs: 50,
maxVUs: 500,
stages: [
{ target: 50, duration: '1m' },
{ target: 100, duration: '2m' },
{ target: 200, duration: '2m' },
{ target: 400, duration: '2m' },
{ target: 0, duration: '30s' },
],
},
},
};A stress test should show where error rates spike or response times become unacceptable. That's your system's current limit.
Soak Test
Runs moderate load for an extended period to detect memory leaks, connection pool exhaustion, and slow degradation.
export const options = {
scenarios: {
soak: {
executor: 'constant-vus',
vus: 50,
duration: '4h', // run for hours
},
},
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};Watch http_req_duration over time — a gradual upward trend indicates resource exhaustion or memory leaks.
Breakpoint Test
Increments load until something breaks, identifying the system's maximum capacity.
export const options = {
scenarios: {
breakpoint: {
executor: 'ramping-arrival-rate',
startRate: 1,
timeUnit: '1s',
preAllocatedVUs: 100,
maxVUs: 1000,
stages: [
{ target: 1000, duration: '30m' }, // slow ramp to max
],
},
},
thresholds: {
http_req_failed: [{ threshold: 'rate<0.1', abortOnFail: true }],
},
};abortOnFail: true stops the test when the threshold is breached — useful for automated breakpoint detection.
Running Multiple Scenarios
Run different load patterns simultaneously:
export const options = {
scenarios: {
reads: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '2m',
preAllocatedVUs: 50,
exec: 'readScenario',
},
writes: {
executor: 'constant-arrival-rate',
rate: 10,
timeUnit: '1s',
duration: '2m',
preAllocatedVUs: 10,
exec: 'writeScenario',
},
},
};
export function readScenario() {
http.get('https://api.example.com/items');
sleep(0.5);
}
export function writeScenario() {
http.post('https://api.example.com/items', JSON.stringify({ name: 'item' }), {
headers: { 'Content-Type': 'application/json' },
});
sleep(1);
}Scenario-Specific Thresholds
Apply thresholds per scenario using tags:
export const options = {
scenarios: {
reads: {
executor: 'constant-vus',
vus: 20,
duration: '1m',
tags: { scenario: 'reads' },
},
},
thresholds: {
'http_req_duration{scenario:reads}': ['p(95)<200'],
},
};Pairing Performance Tests with Functional Tests
k6 scenarios tell you how fast your system responds under load. They don't verify that responses are correct. HelpMeTest handles the behavioral side — AI-powered Robot Framework tests that verify user journeys work correctly, with 24/7 monitoring.
Run your k6 scenarios in CI for performance gates, and HelpMeTest for continuous functional verification.
Try HelpMeTest free — 10 tests included, monitoring every 5 minutes.