Documentation Index Fetch the complete documentation index at: https://mintlify.com/grafana/k6-docs/llms.txt
Use this file to discover all available pages before exploring further.
Scenarios provide fine-grained control over how k6 executes your test workload. They let you model diverse traffic patterns and complex user behaviors in a single test.
What are scenarios?
Scenarios configure how VUs (virtual users) and iterations are scheduled during test execution. Each scenario can:
Execute a different JavaScript function
Use a different executor to control workload patterns
Run in parallel or sequence with other scenarios
Have independent environment variables and tags
Why use scenarios?
Scenarios provide several benefits over simple VU/duration configuration:
Realistic traffic patterns Model different user behaviors with arrival rates, iterations, or ramping patterns.
Multiple workloads Run different test functions in parallel or sequence within one test.
Better organization Separate different test phases or user types into distinct scenarios.
Granular analysis Tag and analyze each scenario independently with scenario-specific metrics.
Basic scenario configuration
Define scenarios in the options.scenarios object:
import http from 'k6/http' ;
export const options = {
scenarios: {
my_scenario: {
executor: 'constant-vus' ,
vus: 10 ,
duration: '30s' ,
},
},
};
export default function () {
http . get ( 'https://test.k6.io/' );
}
Each scenario requires:
A unique name (my_scenario)
An executor property specifying the workload pattern
Executor-specific configuration (like vus and duration)
Scenario executors
Executors determine how k6 schedules VUs and iterations. Choose the executor that best matches your workload:
By iterations
shared-iterations
per-vu-iterations
A fixed number of iterations shared between VUs. export const options = {
scenarios: {
shared_iters: {
executor: 'shared-iterations' ,
vus: 10 ,
iterations: 100 , // Total across all VUs
},
},
};
Use when: You need exactly 100 iterations, distributed among VUs.Each VU runs a fixed number of iterations. export const options = {
scenarios: {
per_vu_iters: {
executor: 'per-vu-iterations' ,
vus: 10 ,
iterations: 10 , // Per VU
},
},
};
Use when: Each VU should run exactly 10 iterations (100 total).
By VUs
A constant number of VUs for a duration. export const options = {
scenarios: {
constant_load: {
executor: 'constant-vus' ,
vus: 50 ,
duration: '1m' ,
},
},
};
Use when: You want steady load with 50 VUs for 1 minute.VUs ramp up and down over stages. export const options = {
scenarios: {
ramping_load: {
executor: 'ramping-vus' ,
startVUs: 0 ,
stages: [
{ duration: '1m' , target: 50 }, // Ramp up
{ duration: '3m' , target: 50 }, // Stay
{ duration: '1m' , target: 0 }, // Ramp down
],
},
},
};
Use when: You need gradual load increases and decreases.
By arrival rate
constant-arrival-rate
ramping-arrival-rate
Fixed iteration rate (open model). export const options = {
scenarios: {
constant_rate: {
executor: 'constant-arrival-rate' ,
rate: 100 , // 100 iterations per timeUnit
timeUnit: '1s' , // Per second
duration: '1m' ,
preAllocatedVUs: 50 , // Initial VUs
maxVUs: 100 , // Maximum VUs
},
},
};
Use when: You need exactly 100 requests per second, regardless of response time.Variable iteration rate over stages. export const options = {
scenarios: {
ramping_rate: {
executor: 'ramping-arrival-rate' ,
startRate: 0 ,
timeUnit: '1s' ,
preAllocatedVUs: 50 ,
maxVUs: 200 ,
stages: [
{ duration: '1m' , target: 50 }, // Ramp to 50/s
{ duration: '3m' , target: 50 }, // Stay at 50/s
{ duration: '1m' , target: 0 }, // Ramp down
],
},
},
};
Use when: You need gradual changes in request rate.
Scenario options
All scenarios support these common options:
Option Type Description Default executorstring Executor type (required) - startTimestring Delay before starting "0s"gracefulStopstring Time to wait for iterations to finish "30s"execstring Function name to execute "default"envobject Scenario-specific environment variables {}tagsobject Scenario-specific tags {}
Multiple scenarios
Run multiple scenarios in parallel:
import http from 'k6/http' ;
export const options = {
scenarios: {
load_test: {
executor: 'constant-vus' ,
vus: 50 ,
duration: '5m' ,
exec: 'loadTest' ,
},
stress_test: {
executor: 'ramping-vus' ,
startTime: '5m' , // Start after load_test
startVUs: 0 ,
stages: [
{ duration: '2m' , target: 100 },
{ duration: '5m' , target: 100 },
{ duration: '2m' , target: 0 },
],
exec: 'stressTest' ,
},
},
};
export function loadTest () {
http . get ( 'https://test.k6.io/' );
}
export function stressTest () {
http . get ( 'https://test.k6.io/' );
http . post ( 'https://test.k6.io/login' , { username: 'test' });
}
This runs a load test for 5 minutes, then immediately starts a stress test.
Tag metrics by scenario:
import http from 'k6/http' ;
export const options = {
scenarios: {
api_test: {
executor: 'constant-vus' ,
vus: 10 ,
duration: '1m' ,
tags: {
test_type: 'api' ,
priority: 'high' ,
},
env: {
BASE_URL: 'https://api.example.com' ,
},
},
web_test: {
executor: 'constant-vus' ,
vus: 20 ,
duration: '1m' ,
tags: {
test_type: 'web' ,
priority: 'medium' ,
},
env: {
BASE_URL: 'https://www.example.com' ,
},
},
},
thresholds: {
'http_req_duration{test_type:api}' : [ 'p(95)<500' ],
'http_req_duration{test_type:web}' : [ 'p(95)<1000' ],
},
};
export default function () {
http . get ( __ENV . BASE_URL );
}
Complete example
Here’s a realistic scenario combining multiple patterns:
import http from 'k6/http' ;
import { sleep , check } from 'k6' ;
export const options = {
scenarios: {
// Warm-up phase
warmup: {
executor: 'constant-vus' ,
vus: 5 ,
duration: '30s' ,
tags: { phase: 'warmup' },
},
// Main load test
load: {
executor: 'ramping-vus' ,
startTime: '30s' ,
startVUs: 0 ,
stages: [
{ duration: '1m' , target: 50 },
{ duration: '5m' , target: 50 },
{ duration: '1m' , target: 0 },
],
tags: { phase: 'load' },
},
// Spike test
spike: {
executor: 'constant-arrival-rate' ,
startTime: '7m30s' ,
rate: 100 ,
timeUnit: '1s' ,
duration: '30s' ,
preAllocatedVUs: 100 ,
maxVUs: 200 ,
tags: { phase: 'spike' },
},
},
thresholds: {
'http_req_duration{phase:load}' : [ 'p(95)<500' ],
'http_req_duration{phase:spike}' : [ 'p(95)<1000' ],
'http_req_failed' : [ 'rate<0.01' ],
},
};
const baseUrl = 'https://quickpizza.grafana.com' ;
export default function () {
const res = http . get ( baseUrl );
check ( res , {
'status is 200' : ( r ) => r . status === 200 ,
});
sleep ( 1 );
}
Scenario execution output
When running scenarios, k6 displays scenario information:
scenarios: (100.00%) 2 scenarios, 20 max VUs, 10m40s max duration:
* shared_iter_scenario: 100 iterations shared among 10 VUs
* per_vu_scenario: 10 iterations for each of 10 VUs (startTime: 10s )
Results are shown both overall and per-scenario:
█ TOTAL RESULTS
http_req_duration..........: avg=115.25ms
http_reqs..................: 200
█ SCENARIO: shared_iter_scenario
http_req_duration..........: avg=115.65ms
http_reqs..................: 100
█ SCENARIO: per_vu_scenario
http_req_duration..........: avg=114.86ms
http_reqs..................: 100
Best practices
Choose the right executor Use VU-based executors for closed models, arrival-rate executors for open models.
Use meaningful names Name scenarios descriptively: “warmup”, “load_test”, “spike” not “scenario1”.
Tag for analysis Add scenario-specific tags to enable granular threshold and analysis.
Sequence with startTime Use startTime to create sequential test phases within one test run.
Next steps
Executors Reference Detailed documentation for each executor type with advanced options