> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/grafana/k6/llms.txt
> Use this file to discover all available pages before exploring further.

# Virtual Users (VUs)

> Learn how k6 uses virtual users to simulate concurrent load on your system.

Virtual Users (VUs) are the core concept in k6 load testing. A VU executes your test script repeatedly, simulating a real user interacting with your system.

## What is a Virtual User?

A VU is a virtualized user that runs your test script. Each VU executes independently and concurrently, running the `default` function in a loop.

```javascript theme={null}
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  vus: 10,        // 10 virtual users
  duration: '30s', // running for 30 seconds
};

export default function() {
  // Each VU executes this repeatedly
  http.get('https://quickpizza.grafana.com/');
  sleep(1);
}
```

With 10 VUs and a 1-second sleep, this test generates approximately 10 requests per second.

## VU Lifecycle

Based on the k6 source code in `lib/execution.go`, VUs have a sophisticated lifecycle:

### Initialization

From `lib/execution.go`, k6 tracks VU initialization:

* `initializedVUs` - Total number of initialized VUs
* `activeVUs` - VUs currently executing the test script
* VU IDs start from 1 (for backwards compatibility)
* Each VU gets unique local and global identifiers

```javascript theme={null}
import exec from 'k6/execution';

export default function() {
  console.log(`VU ${exec.vu.idInTest} in iteration ${exec.vu.iterationInScenario}`);
}
```

### VU States

A VU transitions through these states:

<Steps>
  <Step title="Initialized">
    VU is created and ready to use, stored in the VU buffer.
  </Step>

  <Step title="Active">
    VU is retrieved from the buffer and executing the default function.
  </Step>

  <Step title="Returned">
    VU completes an iteration and returns to the buffer for reuse.
  </Step>
</Steps>

## Planned vs Unplanned VUs

k6 distinguishes between two types of VUs based on the source code in `lib/execution.go`:

### Planned VUs

Planned VUs are pre-initialized before the test starts. They're allocated based on your executor configuration.

```javascript theme={null}
export const options = {
  scenarios: {
    contacts: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '30s', target: 50 },  // These 50 VUs are "planned"
        { duration: '1m', target: 50 },
        { duration: '30s', target: 0 },
      ],
    },
  },
};
```

### Unplanned VUs

Unplanned VUs are initialized dynamically when arrival-rate executors need more VUs than pre-allocated.

```javascript theme={null}
export const options = {
  scenarios: {
    breakpoint: {
      executor: 'ramping-arrival-rate',
      preAllocatedVUs: 50,   // Planned VUs
      maxVUs: 200,           // Up to 200 total (150 can be unplanned)
      startRate: 50,
      timeUnit: '1s',
      stages: [
        { duration: '2m', target: 100 },
      ],
    },
  },
};
```

<Note>
  When k6 initializes an unplanned VU, you'll see: "Initializing an unplanned VU, this may affect test results"
</Note>

## VU Execution Model

From `lib/executor/shared_iterations.go`, k6 implements a sophisticated VU execution model:

### VU Retrieval

The `GetPlannedVU()` function retrieves VUs from a shared buffer channel:

* VUs wait in a buffer channel when not in use
* Executors borrow VUs when needed
* VUs are returned to the buffer after use
* If retrieval takes longer than 400ms, k6 logs a warning

### VU Activation

When a VU starts an iteration:

1. It's retrieved from the buffer
2. The `activeVUs` counter increments
3. It executes the default function
4. The `activeVUs` counter decrements
5. It returns to the buffer

## Configuring VUs

### Constant VUs

Maintain a fixed number of VUs for the entire test:

```javascript theme={null}
export const options = {
  vus: 10,
  duration: '5m',
};
```

### Ramping VUs

Gradually increase or decrease VUs using stages from `examples/stages.js`:

```javascript theme={null}
export const options = {
  stages: [
    // Ramp-up from 1 to 5 VUs in 10s
    { duration: '10s', target: 5 },
    
    // Stay at rest on 5 VUs for 5s
    { duration: '5s', target: 5 },
    
    // Ramp-down from 5 to 0 VUs for 5s
    { duration: '5s', target: 0 },
  ],
};
```

### Per-Scenario VUs

Different scenarios can use different VU configurations:

```javascript theme={null}
export const options = {
  scenarios: {
    light_load: {
      executor: 'constant-vus',
      vus: 10,
      duration: '5m',
    },
    heavy_load: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 100 },
      ],
      startTime: '5m', // Starts after light_load
    },
  },
};
```

## VU Metrics

k6 automatically emits VU-related metrics defined in `metrics/builtin.go`:

### `vus`

Current number of active VUs (Gauge metric):

```javascript theme={null}
export const options = {
  thresholds: {
    vus: ['value>0'], // Ensure VUs are running
  },
};
```

### `vus_max`

Maximum number of VUs initialized during the test (Gauge metric).

## VU Iterations

From `lib/execution.go`, k6 tracks two types of iterations:

### Full Iterations

Iterations that complete normally are counted in `fullIterationsCount`:

```javascript theme={null}
import { Counter } from 'k6/metrics';

const myIterations = new Counter('my_iterations');

export default function() {
  // Complete iteration
  myIterations.add(1);
}
```

### Interrupted Iterations

Iterations cut short by:

* Test duration ending
* VU ramping down
* Manual test interruption (Ctrl+C)
* Threshold failures with `abortOnFail`

These are tracked in `interruptedIterationsCount` and reflected in the `dropped_iterations` metric.

## VU Resources and Memory

Each VU maintains its own:

* JavaScript runtime (Sobek/goja)
* Execution context
* Variable scope
* Iteration state

<Warning>
  VUs are resource-intensive. A single machine typically handles 100-1000 VUs depending on test complexity and hardware.
</Warning>

## VU Execution Patterns

### Shared Iterations

From `lib/executor/shared_iterations.go`, VUs share a total iteration count:

```javascript theme={null}
export const options = {
  scenarios: {
    shared: {
      executor: 'shared-iterations',
      vus: 10,
      iterations: 100, // 100 total iterations shared among 10 VUs
    },
  },
};
```

Each VU attempts iterations until the total is reached.

### Per-VU Iterations

Each VU executes a specific number of iterations:

```javascript theme={null}
export const options = {
  scenarios: {
    per_vu: {
      executor: 'per-vu-iterations',
      vus: 10,
      iterations: 10, // Each VU does 10 iterations = 100 total
    },
  },
};
```

## Advanced VU Concepts

### VU Identifiers

From `lib/execution.go`, each VU receives unique identifiers:

```javascript theme={null}
import exec from 'k6/execution';

export default function() {
  // Local VU ID (unique within this k6 instance)
  console.log(`Local VU ID: ${exec.vu.idInInstance}`);
  
  // Global VU ID (unique across distributed k6 instances)
  console.log(`Global VU ID: ${exec.vu.idInTest}`);
}
```

### VU Tags

All metrics emitted by a VU are automatically tagged:

```javascript theme={null}
import http from 'k6/http';

export default function() {
  // Automatically tagged with VU ID
  http.get('https://quickpizza.grafana.com/');
}
```

### Graceful Stop

When ramping down, VUs complete their current iteration before stopping:

```javascript theme={null}
export const options = {
  scenarios: {
    ramping: {
      executor: 'ramping-vus',
      gracefulStop: '30s', // Allow up to 30s for iterations to complete
      stages: [
        { duration: '1m', target: 100 },
        { duration: '10s', target: 0 }, // Ramp down
      ],
    },
  },
};
```

<Tip>
  Set `gracefulStop` longer than your longest iteration to avoid interruptions.
</Tip>

## Best Practices

<Steps>
  <Step title="Start Small">
    Begin with a small number of VUs and gradually increase to find limits.
  </Step>

  <Step title="Use Stages for Realistic Load">
    Ramp up VUs gradually to simulate realistic traffic patterns.
  </Step>

  <Step title="Monitor VU Metrics">
    Watch `vus` and `vus_max` to ensure your test runs as expected.
  </Step>

  <Step title="Consider Arrival Rates">
    For consistent throughput, use arrival-rate executors instead of VU-based executors.
  </Step>
</Steps>

## Common Patterns

### Load Test Pattern

```javascript theme={null}
export const options = {
  stages: [
    { duration: '5m', target: 100 },  // Ramp up
    { duration: '10m', target: 100 }, // Stay at peak
    { duration: '5m', target: 0 },    // Ramp down
  ],
};
```

### Stress Test Pattern

```javascript theme={null}
export const options = {
  stages: [
    { duration: '2m', target: 100 },
    { duration: '5m', target: 100 },
    { duration: '2m', target: 200 },
    { duration: '5m', target: 200 },
    { duration: '2m', target: 300 },
    { duration: '5m', target: 300 },
    { duration: '10m', target: 0 },
  ],
};
```

Virtual Users are the foundation of k6 load testing. Understanding how they work enables you to design effective, realistic load tests.
