Skip to main content
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:
  1. A fixed number of VUs start executing
  2. Each VU runs iterations in a loop
  3. Execution continues for the specified duration
  4. VUs run as many iterations as they can complete
  5. All VUs stop when duration expires (plus gracefulStop)
VUs ^  
  3 |████████████████████████████
  2 |████████████████████████████
  1 |████████████████████████████
  0 +-----------------------------> time
    0s        duration         end

Configuration

executor
string
required
Must be constant-vus
vus
integer
default:"1"
Number of VUs to run concurrently. Must be greater than 0.
duration
duration
required
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

FeatureConstant VUsRamping VUsShared Iterations
VU countFixedVariableFixed
DurationFixedSum of stagesUp to maxDuration
IterationsUnlimitedUnlimitedFixed total
Best forSteady loadVariable loadFixed work
PredictabilityHighMediumLow (per VU)

Best Practices

  1. Set realistic duration: Ensure duration matches your test objectives
  2. Include think time: Use sleep() to simulate realistic user behavior
  3. Monitor resource usage: Watch system resources throughout the constant load
  4. Use for baselines: Great for establishing performance baselines before ramping tests
  5. Configure gracefulStop: Allow enough time for iterations to complete cleanly
  6. 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

Build docs developers (and LLMs) love