Choosing Between VU-Based and Arrival Rate Executors in k6
Learn how to choose between VU-based and Arrival Rate executors in k6 to ensure your load tests accurately simulate user behavior and system throughput.
12 May 2026, 07:13 UTC

The Throughput Dilemma: VUs vs. Arrival Rate
When designing a load test, the most critical decision is whether you want to control the number of concurrent users or the number of requests per second. Choosing the wrong executor can lead to misleading results: as your system under test (SUT) slows down, a VU-based test will naturally drop its throughput, potentially masking the very performance bottleneck you are trying to find.
The takeaway is simple: use VU executors to test resource contention and session-based limits; use Arrival Rate executors to test a specific throughput target regardless of how the system responds.
Comparing Execution Models
| Feature | VU-Based (Closed Model) | Arrival Rate (Open Model) |
|---|---|---|
| Primary Control | Number of concurrent users | Iterations per time unit |
| Throughput Behavior | Fluctuates based on response time | Remains constant (until VU exhaustion) |
| System Stress | Limited by VU count | Can increase if responses slow down |
| Best Use Case | Simulating a fixed set of employees/users | Simulating public API traffic or web spikes |
Trade-offs and Technical Constraints
The Closed Model (VU-Based): In this mode, a Virtual User (VU) executes a script, waits for the response, and then starts the next iteration. If the server response time increases from 100ms to 500ms, the VU spends more time waiting and fewer requests are sent per second. This is a "closed model" because the arrival of a new request depends on the completion of the previous one.
The Open Model (Arrival Rate): This decouples the request rate from the response time. k6 attempts to start a specific number of iterations per second regardless of when previous iterations finish. This is an "open model," mimicking real-world traffic where users arrive independently of each other.
The VU Pool Risk: Arrival Rate executors require a pool of VUs to execute the requested iterations. If your SUT slows down, k6 needs more VUs to maintain the target arrival rate because each VU is occupied for longer. If the preAllocatedVUs limit is reached, k6 will fail to start new iterations, and you will see warnings in the logs regarding VU pool exhaustion.
Implementation: Constant Arrival Rate
To implement a stable throughput test, use the constant-arrival-rate executor. This configuration ensures that k6 targets a specific number of iterations per second, utilizing a pre-allocated pool of VUs to avoid the overhead of initializing VUs during the test run.
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
scenarios: {
stable_throughput: {
executor: 'constant-arrival-rate',
rate: 50, // Target 50 iterations per second
timeUnit: '1s', // The time unit for the rate
duration: '5m', // Total test duration
preAllocatedVUs: 100, // VUs initialized at start
maxVUs: 200, // Maximum VUs allowed to scale to
},
},
};
export default function () {
http.get('https://api.example.com/health');
// Note: sleep() in arrival-rate executors affects how many VUs are needed,
// but not the overall arrival rate of the iterations.
}
Execution and Validation
Run the test from your terminal with the following command (assuming you have k6 installed and have network access to the target):
k6 run script.js
Verification Steps:
- Check Throughput: Observe the
http_reqsmetric in the output. In aconstant-arrival-ratetest, this should remain steady even ifhttp_req_duration(latency) increases. - Monitor VU Usage: Watch the
vusmetric. If the number of VUs climbs steadily toward themaxVUslimit, it indicates your SUT is slowing down, and k6 is allocating more users to maintain the rate. - Log Audit: Scan the console output for
VU pool exhausted. If this appears, you must increasemaxVUsor optimize the SUT.
Limitations and Rollback
The primary limitation of the Arrival Rate executor is memory consumption. Because k6 may allocate hundreds or thousands of VUs to maintain a rate during a system slowdown, the load generator machine may run out of RAM.
Since this operation is a read-only load test, there is no state change to the k6 environment to roll back. However, if the test causes a Denial of Service (DoS) on your SUT, immediately terminate the process using Ctrl+C to stop all active VU connections.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.