The constant-vus executor runs a fixed number of VUs for a specified duration. This is one of the simplest executors and is ideal for steady-state load testing where you want to maintain constant concurrent users.
How It Works
With constant VUs:
- A fixed number of VUs start executing
- Each VU runs iterations in a loop
- Execution continues for the specified duration
- VUs run as many iterations as they can complete
- All VUs stop when duration expires (plus gracefulStop)
VUs ^
3 |████████████████████████████
2 |████████████████████████████
1 |████████████████████████████
0 +-----------------------------> time
0s duration end
Configuration
Number of VUs to run concurrently. Must be greater than 0.
Total duration of the executor. Must be at least 1 second.
Example
Basic Load Test
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
scenarios: {
constant_load: {
executor: 'constant-vus',
vus: 10,
duration: '5m',
},
},
};
export default function () {
http.get('https://test.k6.io');
sleep(1);
}
This maintains 10 concurrent VUs for 5 minutes, each VU continuously looping through iterations.
With Thresholds
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
scenarios: {
steady_state: {
executor: 'constant-vus',
vus: 50,
duration: '10m',
},
},
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
},
};
export default function () {
http.get('https://test.k6.io/api/data');
sleep(1);
}
Multiple Constant Load Scenarios
export const options = {
scenarios: {
light_load: {
executor: 'constant-vus',
vus: 5,
duration: '2m',
startTime: '0s',
tags: { load_level: 'light' },
},
medium_load: {
executor: 'constant-vus',
vus: 20,
duration: '5m',
startTime: '2m',
tags: { load_level: 'medium' },
},
heavy_load: {
executor: 'constant-vus',
vus: 50,
duration: '3m',
startTime: '7m',
tags: { load_level: 'heavy' },
},
},
};
When to Use
Use the constant VUs executor when:
- You want to maintain a steady load on your system
- You’re testing how the system performs under sustained concurrent users
- You need predictable, consistent load over time
- You’re establishing performance baselines
- You want to verify system stability under constant load
- You’re running soak tests to detect memory leaks or degradation
Behavior Details
Iteration Loop
Each VU continuously executes the default function in a loop until duration expires:
export default function () {
// This function runs repeatedly for each VU
// VU 1: iteration 0, 1, 2, 3, 4...
// VU 2: iteration 0, 1, 2, 3, 4...
console.log(`VU ${__VU}, iteration ${__ITER}`);
}
No Fixed Iteration Count
Unlike iteration-based executors, there’s no predetermined number of iterations. VUs run as many iterations as they can:
// Fast iterations (100ms each) might complete 600 iterations in 1 minute
// Slow iterations (5s each) might complete 12 iterations in 1 minute
Graceful Stop
When duration expires, running iterations have gracefulStop time to complete:
export const options = {
scenarios: {
example: {
executor: 'constant-vus',
vus: 10,
duration: '5m',
gracefulStop: '30s', // Allow 30s for iterations to finish
},
},
};
Execution timeline:
- 0s - 5m: Regular execution, new iterations can start
- 5m - 5m30s: Graceful stop period, no new iterations, existing ones finish
- 5m30s: All VUs forcefully stopped
Metrics
The executor emits these metrics:
iterations - Total completed iterations
iteration_duration - Time to complete each iteration
vus - Number of active VUs (constant at configured value)
vus_max - Maximum number of VUs (same as vus)
Common Patterns
Soak Testing
Test system stability over extended periods:
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
scenarios: {
soak: {
executor: 'constant-vus',
vus: 20,
duration: '4h', // Long duration to detect leaks
},
},
};
export default function () {
http.get('https://test.k6.io');
sleep(1);
}
Steady Background Load
Maintain background load while testing something else:
export const options = {
scenarios: {
background: {
executor: 'constant-vus',
vus: 10,
duration: '30m',
exec: 'background',
},
spike: {
executor: 'ramping-vus',
startTime: '10m',
startVUs: 0,
stages: [
{ duration: '1m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '1m', target: 0 },
],
exec: 'spike',
},
},
};
export function background() {
http.get('https://test.k6.io/background');
}
export function spike() {
http.get('https://test.k6.io/spike');
}
Different Iteration Speeds
import http from 'k6/http';
import { sleep } from 'k6';
import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
export const options = {
scenarios: {
variable_think_time: {
executor: 'constant-vus',
vus: 25,
duration: '10m',
},
},
};
export default function () {
http.get('https://test.k6.io');
// Simulate realistic user think time variation
sleep(randomIntBetween(1, 5));
}
Comparison with Other Executors
| Feature | Constant VUs | Ramping VUs | Shared Iterations |
|---|
| VU count | Fixed | Variable | Fixed |
| Duration | Fixed | Sum of stages | Up to maxDuration |
| Iterations | Unlimited | Unlimited | Fixed total |
| Best for | Steady load | Variable load | Fixed work |
| Predictability | High | Medium | Low (per VU) |
Best Practices
- Set realistic duration: Ensure duration matches your test objectives
- Include think time: Use
sleep() to simulate realistic user behavior
- Monitor resource usage: Watch system resources throughout the constant load
- Use for baselines: Great for establishing performance baselines before ramping tests
- Configure gracefulStop: Allow enough time for iterations to complete cleanly
- Consider iteration duration: Faster iterations = more iterations = more load
Duration Requirements
The duration must be at least 1 second. For very short tests, consider using iteration-based executors instead.
// ❌ Invalid - duration too short
{
executor: 'constant-vus',
vus: 10,
duration: '500ms', // Error!
}
// ✓ Valid
{
executor: 'constant-vus',
vus: 10,
duration: '1s', // OK
}
See Also