> ## 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.

# Externally Controlled Executor

> Control test execution dynamically via the k6 REST API

The `externally-controlled` executor allows you to control k6 execution in real-time via the REST API. You can dynamically scale VUs up or down, pause and resume execution, and adjust test parameters while the test is running.

## How It Works

With externally controlled execution:

1. Test starts with initial `vus` and runs for `duration` (or indefinitely if duration is 0)
2. VUs continuously loop through iterations
3. You can control execution via the k6 REST API:
   * Scale VUs up or down
   * Pause and resume the test
   * Update the configuration dynamically
4. Test runs until duration expires or you stop it manually

```
VUs ^
 50 |            API: scale to 50
 30 |     ╱‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾╲
 10 |‾‾‾‾╯                 ╲___
  0 +-----------------------------------> time
      ^         ^          ^    ^
      start   scale up   scale down
              (via API)  (via API)
```

## Configuration

<ParamField path="executor" type="string" required>
  Must be `externally-controlled`
</ParamField>

<ParamField path="vus" type="integer" default="0">
  Initial number of VUs. Can be 0. Cannot be negative.
</ParamField>

<ParamField path="maxVUs" type="integer" default="vus">
  Maximum number of VUs that can be used during the test. Once set at test start, this cannot be decreased (only increased via API).
</ParamField>

<ParamField path="duration" type="duration" required>
  Maximum test duration. Use `0` for infinite duration (test runs until manually stopped). Cannot be negative.
</ParamField>

<Note>
  The `gracefulStop` option is NOT supported by this executor. VUs stop immediately when the test ends.
</Note>

## Example

### Basic Setup

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

export const options = {
  scenarios: {
    external: {
      executor: 'externally-controlled',
      vus: 10,
      maxVUs: 100,
      duration: '30m',  // Or '0' for infinite
    },
  },
};

export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}
```

Run with the API enabled:

```bash theme={null}
k6 run --http-debug script.js
```

### Infinite Duration Test

```javascript theme={null}
export const options = {
  scenarios: {
    external: {
      executor: 'externally-controlled',
      vus: 1,
      maxVUs: 50,
      duration: '0',  // Runs until manually stopped
    },
  },
};
```

### Start Paused

```bash theme={null}
k6 run --paused script.js
```

Then control via API to resume when ready.

## API Control

The k6 REST API (enabled with `--http-debug` or environment variable) provides endpoints to control execution.

### Get Current Status

```bash theme={null}
curl http://localhost:6565/v1/status
```

Response:

```json theme={null}
{
  "data": {
    "attributes": {
      "paused": false,
      "vus": 10,
      "vus-max": 100,
      "stopped": false,
      "running": true,
      "tainted": false
    }
  }
}
```

### Scale VUs

```bash theme={null}
curl -X PATCH http://localhost:6565/v1/status \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "attributes": {
        "vus": 50
      }
    }
  }'
```

### Pause Test

```bash theme={null}
curl -X PATCH http://localhost:6565/v1/status \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "attributes": {
        "paused": true
      }
    }
  }'
```

### Resume Test

```bash theme={null}
curl -X PATCH http://localhost:6565/v1/status \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "attributes": {
        "paused": false
      }
    }
  }'
```

### Scale and Adjust MaxVUs

```bash theme={null}
curl -X PATCH http://localhost:6565/v1/status \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "attributes": {
        "vus": 80,
        "vus-max": 200
      }
    }
  }'
```

## When to Use

Use the externally controlled executor when:

* You need dynamic control during test execution
* You're integrating k6 with external monitoring/alerting systems
* You want to adjust load based on real-time application metrics
* You're building custom load testing dashboards
* You need to manually respond to test conditions
* You want to run exploratory performance testing
* You're implementing adaptive load testing algorithms

## Behavior Details

### Initial VUs and MaxVUs

At test start, k6 initializes `maxVUs` and activates `vus` of them:

```javascript theme={null}
{
  vus: 10,      // 10 VUs active
  maxVUs: 100,  // 100 VUs initialized and ready
}
// 10 VUs are running, 90 are initialized but idle
```

### Dynamic Scaling

Via API, you can scale `vus` up to `maxVUs`:

```javascript theme={null}
// Start: vus=10, maxVUs=100
// API request: vus=50
// Result: 40 additional VUs become active (total 50 active)
```

### MaxVUs Constraints

<Warning>
  You can increase `maxVUs` via API but cannot decrease it below the initial configuration value.
</Warning>

```javascript theme={null}
// Initial config
{ vus: 10, maxVUs: 100 }

// ✓ Valid via API: increase maxVUs
{ vus: 50, maxVUs: 200 }

// ❌ Invalid via API: decrease maxVUs below initial
{ vus: 10, maxVUs: 50 }  // Error! Cannot go below 100
```

### Duration Behavior

**Finite duration:**

```javascript theme={null}
{ duration: '30m' }
// Test runs for 30 minutes then stops
// Cannot change duration via API
```

**Infinite duration:**

```javascript theme={null}
{ duration: '0' }
// Test runs until you stop it (Ctrl+C or API)
// No automatic stop
```

### Pause Behavior

When paused:

* Running iterations complete
* No new iterations start
* VUs remain allocated
* Test timer continues (for non-zero duration)

```javascript theme={null}
// Timeline with pause:
// 0-5m: Running normally
// 5-10m: Paused (no new iterations)
// 10-30m: Running normally
// If duration is 30m, test still ends at 30m total
```

### Not Distributable

<Warning>
  The externally-controlled executor does NOT support distributed execution (execution segments). It can only run on a single k6 instance.
</Warning>

## Common Patterns

### Integration with Monitoring

```bash theme={null}
#!/bin/bash
# Scale based on response time

while true; do
  # Get current p95 from your monitoring system
  p95=$(curl -s http://monitoring.example.com/api/p95)
  
  if [ "$p95" -lt 1000 ]; then
    # Response time good, increase load
    curl -X PATCH http://localhost:6565/v1/status \
      -H 'Content-Type: application/json' \
      -d '{"data":{"attributes":{"vus":50}}}'
  else
    # Response time degrading, reduce load
    curl -X PATCH http://localhost:6565/v1/status \
      -H 'Content-Type: application/json' \
      -d '{"data":{"attributes":{"vus":20}}}'
  fi
  
  sleep 30
done
```

### Manual Exploratory Testing

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

export const options = {
  scenarios: {
    manual: {
      executor: 'externally-controlled',
      vus: 0,        // Start with no load
      maxVUs: 500,   // Allow scaling up to 500
      duration: '0', // Run indefinitely
    },
  },
};

export default function () {
  http.get('https://test.k6.io/api');
  sleep(1);
}
```

Then manually control:

```bash theme={null}
# Start at 10 VUs to warm up
curl -X PATCH http://localhost:6565/v1/status -d '{"data":{"attributes":{"vus":10}}}'

# Observe for 2 minutes...

# Scale to 50 VUs
curl -X PATCH http://localhost:6565/v1/status -d '{"data":{"attributes":{"vus":50}}}'

# If system handles it well, scale to 100
curl -X PATCH http://localhost:6565/v1/status -d '{"data":{"attributes":{"vus":100}}}'
```

### Dashboard Integration

```javascript theme={null}
// React/Node.js dashboard example
const scaleVUs = async (vuCount) => {
  await fetch('http://localhost:6565/v1/status', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      data: {
        attributes: {
          vus: vuCount
        }
      }
    })
  });
};

const togglePause = async (paused) => {
  await fetch('http://localhost:6565/v1/status', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      data: {
        attributes: {
          paused: paused
        }
      }
    })
  });
};
```

## Validation

### Valid Configurations

```javascript theme={null}
// ✓ Minimal config
{
  executor: 'externally-controlled',
  vus: 1,
  maxVUs: 10,
  duration: '10m',
}

// ✓ Infinite duration
{
  executor: 'externally-controlled',
  vus: 0,
  maxVUs: 100,
  duration: '0',
}

// ✓ maxVUs defaults to vus
{
  executor: 'externally-controlled',
  vus: 50,
  duration: '1h',
  // maxVUs automatically set to 50
}
```

### Invalid Configurations

```javascript theme={null}
// ❌ Negative VUs
{
  vus: -10,  // Error!
}

// ❌ vus > maxVUs
{
  vus: 100,
  maxVUs: 50,  // Error!
}

// ❌ Negative duration
{
  duration: '-5m',  // Error!
}

// ❌ Duration not specified
{
  vus: 10,
  maxVUs: 50,
  // duration: ??? // Error!
}

// ❌ Using gracefulStop
{
  vus: 10,
  duration: '10m',
  gracefulStop: '30s',  // Error! Not supported
}
```

## Metrics

Standard k6 metrics are emitted:

* `iterations` - Total completed iterations
* `iteration_duration` - Time per iteration
* `vus` - Current number of active VUs (changes via API)
* `vus_max` - Current maximum VUs (matches maxVUs config)

## Best Practices

1. **Set high maxVUs**: Allow plenty of headroom for scaling
2. **Start low**: Begin with few VUs and scale up as needed
3. **Monitor carefully**: Watch metrics when scaling to avoid overload
4. **Use infinite duration for exploration**: `duration: '0'` for manual control
5. **Secure the API**: Restrict access to the REST API in production
6. **Gradual scaling**: Avoid jumping directly from 10 to 1000 VUs
7. **Test API integration**: Ensure your control logic works before production
8. **Have a plan**: Define scaling rules/criteria before running the test

## Limitations

<Warning>
  Key limitations of the externally-controlled executor:

  * No distributed execution support
  * Cannot use `gracefulStop`
  * Cannot decrease `maxVUs` below initial value
  * Cannot change `duration` once test starts
  * Cannot pause before test starts (use `--paused` flag instead)
</Warning>

## See Also

* [Ramping VUs](/executors/ramping-vus) - Programmatic VU ramping with stages
* [Constant VUs](/executors/constant-vus) - Simple fixed VU execution
* [k6 REST API Documentation](https://k6.io/docs/misc/k6-rest-api/) - Complete API reference
