Skip to main content
The per-vu-iterations executor ensures that each VU runs an exact number of iterations. Unlike shared iterations where VUs compete for iterations, here every VU completes its own fixed quota of iterations.

How It Works

With per-VU iterations:
  1. Each VU is assigned a fixed number of iterations
  2. VUs run independently without competing
  3. Total iterations = vus × iterations
  4. Each VU completes all its iterations or runs until maxDuration
  5. All VUs complete the same number of iterations (if within maxDuration)
VU 1: [iter 1][iter 2][iter 3]  (3 iterations)
VU 2: [iter 1][iter 2][iter 3]  (3 iterations)  
VU 3: [iter 1][iter 2][iter 3]  (3 iterations)

Total: 9 iterations (3 VUs × 3 iterations each)

Configuration

executor
string
required
Must be per-vu-iterations
vus
integer
default:"1"
Number of VUs to run concurrently. Must be greater than 0.
iterations
integer
default:"1"
Number of iterations each VU executes. Must be greater than 0.
maxDuration
duration
default:"10m"
Maximum duration for the executor. If a VU’s iterations don’t complete within this time, remaining iterations are dropped.

Example

Basic Configuration

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  scenarios: {
    per_vu_scenario: {
      executor: 'per-vu-iterations',
      vus: 10,
      iterations: 20,
      maxDuration: '1m',
    },
  },
};

export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}
This runs 10 VUs, each executing 20 iterations, for a total of 200 iterations.

Per-User Session Simulation

import http from 'k6/http';
import { sleep } from 'k6';
import { check } from 'k6';

export const options = {
  scenarios: {
    user_sessions: {
      executor: 'per-vu-iterations',
      vus: 50,
      iterations: 10, // Each user completes 10 sessions
      maxDuration: '5m',
    },
  },
};

export default function () {
  // Login
  const loginRes = http.post('https://test.k6.io/login', {
    username: `user_${__VU}`,
    password: 'password',
  });
  
  check(loginRes, { 'login successful': (r) => r.status === 200 });
  sleep(1);
  
  // Browse
  http.get('https://test.k6.io/products');
  sleep(2);
  
  // Logout
  http.post('https://test.k6.io/logout');
  sleep(1);
}

When to Use

Use the per-VU iterations executor when:
  • You want consistent work per VU (e.g., each user completes 10 sessions)
  • You need predictable per-VU behavior for testing
  • You’re simulating realistic user behavior where each user does a set amount of work
  • You want the total iterations to scale linearly with VUs
  • You need to test with isolated per-VU data or state

Behavior Details

Total Iterations

The total number of iterations is the product of VUs and iterations per VU:
export const options = {
  scenarios: {
    example: {
      executor: 'per-vu-iterations',
      vus: 10,
      iterations: 5,
      // Total iterations: 10 × 5 = 50
    },
  },
};

VU Isolation

Each VU maintains its own iteration counter and executes independently:
export default function () {
  console.log(`VU ${__VU}, iteration ${__ITER}`);
  // VU 1: iterations 0, 1, 2, 3, 4...
  // VU 2: iterations 0, 1, 2, 3, 4...
  // VU 3: iterations 0, 1, 2, 3, 4...
}

Maximum Duration

If a VU cannot complete all iterations within maxDuration, remaining iterations are dropped:
export const options = {
  scenarios: {
    example: {
      executor: 'per-vu-iterations',
      vus: 5,
      iterations: 100,
      maxDuration: '10s', // May not complete all 100 per VU
    },
  },
};
Dropped iterations are tracked per VU in the dropped_iterations metric.

Scaling Behavior

When using execution segments for distributed testing, only VUs are scaled, NOT iterations per VU. This maintains linear scaling.
Example with 50% execution segment:
// Original config
{
  vus: 10,
  iterations: 20,
  // Total: 200 iterations
}

// With 50% segment (0:0.5)
// Scaled VUs: 5
// Iterations per VU: 20 (unchanged)
// Total: 100 iterations (50% of original)

Metrics

The executor emits these metrics:
  • iterations - Total completed iterations across all VUs
  • iteration_duration - Time to complete each iteration
  • dropped_iterations - Iterations that didn’t complete within maxDuration (tracked per VU)
  • vus - Number of active VUs
  • vus_max - Maximum number of VUs

Common Patterns

User Journey Testing

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  scenarios: {
    user_journey: {
      executor: 'per-vu-iterations',
      vus: 20,
      iterations: 5, // Each user completes journey 5 times
    },
  },
};

export default function () {
  // Complete user journey
  http.get('https://test.k6.io');
  sleep(1);
  
  http.get('https://test.k6.io/products');
  sleep(2);
  
  http.post('https://test.k6.io/cart/add', { productId: 123 });
  sleep(1);
  
  http.post('https://test.k6.io/checkout');
  sleep(1);
}

Per-VU Data Processing

import http from 'k6/http';
import { SharedArray } from 'k6/data';

const users = new SharedArray('users', function () {
  return JSON.parse(open('./users.json'));
});

export const options = {
  scenarios: {
    user_requests: {
      executor: 'per-vu-iterations',
      vus: users.length,
      iterations: 10, // Each user makes 10 requests
    },
  },
};

export default function () {
  const user = users[__VU - 1]; // Each VU gets one user
  http.post('https://test.k6.io/api', JSON.stringify({
    userId: user.id,
    iteration: __ITER,
  }));
}

Ramping Users with Fixed Work

Combine with other executors for complex scenarios:
export const options = {
  scenarios: {
    warmup: {
      executor: 'per-vu-iterations',
      vus: 1,
      iterations: 10,
      startTime: '0s',
    },
    main_load: {
      executor: 'per-vu-iterations',
      vus: 50,
      iterations: 20,
      startTime: '30s', // Start after warmup
    },
  },
};

Comparison with Shared Iterations

FeaturePer VU IterationsShared Iterations
Total iterationsvus × iterationsFixed total
DistributionEven (each VU does same)Dynamic (faster VUs do more)
VU workloadEqualUnequal
PredictabilityHigh per-VUHigh total
ScalingLinear with VUsIndependent of VUs
Use caseConsistent per-user loadFixed total work

Best Practices

  1. Set realistic maxDuration: Ensure each VU has enough time to complete all iterations
  2. Use for user simulation: Great for simulating N users each doing M actions
  3. Monitor per-VU metrics: Use __VU and __ITER to track per-VU behavior
  4. Consider VU reuse: VUs are reused across iterations, so handle state appropriately
  5. Plan for scaling: Remember only VUs scale in distributed testing, not iterations

See Also

Build docs developers (and LLMs) love