Skip to main content
The ramping-vus executor (also known as stages) gradually increases or decreases the number of VUs over defined time periods. This is the most common executor for realistic load testing, as it allows you to simulate traffic patterns that ramp up, sustain, and ramp down.

How It Works

With ramping VUs:
  1. VUs start at startVUs (or ramp from 0)
  2. VUs increase/decrease linearly to reach each stage’s target
  3. Each stage has a duration and target VU count
  4. VUs continuously run iterations throughout all stages
  5. Ramping is smooth and linear between targets
VUs ^
100 |          ╱‾‾‾‾‾‾‾‾╲
 75 |        ╱            ╲
 50 |      ╱                ╲
 25 |    ╱                    ╲
  0 |╺━━╯                      ╲___
    +---------------------------------> time
      2m    5m   3m     2m    2m
      (stages)

Configuration

executor
string
required
Must be ramping-vus
startVUs
integer
default:"1"
Number of VUs to start with. Can be 0.
stages
array
required
Array of stage objects defining the VU ramping pattern. Each stage must have duration and target.
stages[].duration
duration
required
Duration of this stage. Can be 0 for instant VU changes.
stages[].target
integer
required
Target number of VUs at the end of this stage.
gracefulRampDown
duration
default:"30s"
Time to wait for iterations to finish when ramping down VUs. This is separate from gracefulStop.

Example

Classic Load Test Pattern

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

export const options = {
  scenarios: {
    load_test: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 10 },   // Ramp up to 10 VUs over 2 minutes
        { duration: '5m', target: 10 },   // Stay at 10 VUs for 5 minutes
        { duration: '2m', target: 50 },   // Ramp up to 50 VUs over 2 minutes
        { duration: '5m', target: 50 },   // Stay at 50 VUs for 5 minutes
        { duration: '2m', target: 0 },    // Ramp down to 0 VUs over 2 minutes
      ],
      gracefulRampDown: '30s',
    },
  },
};

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

Spike Test

export const options = {
  scenarios: {
    spike: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '10s', target: 10 },   // Baseline
        { duration: '0s', target: 100 },   // Instant spike to 100 VUs
        { duration: '1m', target: 100 },   // Sustain spike
        { duration: '10s', target: 10 },   // Quick recovery
        { duration: '3m', target: 10 },    // Observe recovery
        { duration: '10s', target: 0 },    // Ramp down
      ],
    },
  },
};

Stress Test - Find Breaking Point

export const options = {
  scenarios: {
    stress: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 50 },
        { duration: '2m', target: 100 },
        { duration: '2m', target: 150 },
        { duration: '2m', target: 200 },
        { duration: '2m', target: 250 },
        { duration: '2m', target: 300 },
        { duration: '5m', target: 0 },     // Gradual ramp down
      ],
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.1'],       // Less than 10% errors
    http_req_duration: ['p(95)<2000'],   // 95% under 2s
  },
};

When to Use

Use the ramping VUs executor when:
  • You want to simulate realistic traffic patterns
  • You need to test how your system handles increasing load
  • You’re performing stress testing to find breaking points
  • You want to observe system behavior during ramp-up and ramp-down
  • You need to give your system time to warm up before peak load
  • You’re testing auto-scaling behavior

Behavior Details

Linear Ramping

VUs increase/decrease linearly between targets:
stages: [
  { duration: '60s', target: 60 },
]
// At t=0s:  0 VUs
// At t=10s: 10 VUs
// At t=30s: 30 VUs
// At t=60s: 60 VUs

Zero-Duration Stages

Use duration: '0s' for instant VU changes:
stages: [
  { duration: '1m', target: 10 },
  { duration: '0s', target: 100 },  // Instant jump to 100 VUs
  { duration: '1m', target: 100 },
]

Graceful Ramp Down

When ramping down, VUs get gracefulRampDown time to finish their current iteration:
export const options = {
  scenarios: {
    example: {
      executor: 'ramping-vus',
      startVUs: 100,
      stages: [
        { duration: '5m', target: 100 },
        { duration: '2m', target: 10 },   // Ramping down
      ],
      gracefulRampDown: '30s',  // Each VU has 30s to finish
    },
  },
};

Maximum VUs Reservation

k6 pre-allocates the maximum number of VUs needed across all stages:
stages: [
  { duration: '1m', target: 50 },
  { duration: '2m', target: 100 },  // Peak: 100 VUs
  { duration: '1m', target: 25 },
]
// k6 will initialize 100 VUs at test start

Common Load Test Patterns

Soak Test

Sustained load over a long period:
stages: [
  { duration: '5m', target: 50 },    // Ramp up
  { duration: '3h', target: 50 },    // Soak at 50 VUs for 3 hours
  { duration: '5m', target: 0 },     // Ramp down
]

Breakpoint Test

Continually increase until failure:
stages: [
  { duration: '5m', target: 100 },
  { duration: '5m', target: 200 },
  { duration: '5m', target: 300 },
  { duration: '5m', target: 400 },
  { duration: '5m', target: 500 },
  // Continue until system breaks...
]

Wave Pattern

Simulate traffic waves:
stages: [
  { duration: '30s', target: 20 },   // Wave 1 up
  { duration: '30s', target: 10 },   // Wave 1 down
  { duration: '30s', target: 30 },   // Wave 2 up
  { duration: '30s', target: 15 },   // Wave 2 down
  { duration: '30s', target: 40 },   // Wave 3 up
  { duration: '30s', target: 0 },    // Wave 3 down
]

Advanced Configuration

Multiple Ramping Scenarios

export const options = {
  scenarios: {
    api_traffic: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '5m', target: 50 },
        { duration: '10m', target: 50 },
        { duration: '5m', target: 0 },
      ],
      exec: 'apiTest',
      tags: { scenario: 'api' },
    },
    web_traffic: {
      executor: 'ramping-vus',
      startVUs: 0,
      startTime: '2m',  // Start 2 minutes later
      stages: [
        { duration: '5m', target: 100 },
        { duration: '10m', target: 100 },
        { duration: '5m', target: 0 },
      ],
      exec: 'webTest',
      tags: { scenario: 'web' },
    },
  },
};

export function apiTest() {
  http.get('https://test.k6.io/api');
}

export function webTest() {
  http.get('https://test.k6.io');
}

Metrics

The executor emits these metrics:
  • iterations - Total completed iterations
  • iteration_duration - Time to complete each iteration
  • vus - Current number of active VUs (changes over time)
  • vus_max - Maximum number of VUs (from peak stage)

Validation

The executor validates that VU targets don’t exceed the sanity limit of 100 million VUs.
// ❌ Invalid - exceeds max VUs
stages: [
  { duration: '1m', target: 100000001 }, // Error!
]

// ❌ Invalid - no stages defined
stages: [] // Error!

// ❌ Invalid - negative target
stages: [
  { duration: '1m', target: -10 }, // Error!
]

Best Practices

  1. Start from zero: Begin with startVUs: 0 to see system behavior from cold start
  2. Include ramp-up: Give your system time to warm up (caches, connection pools, etc.)
  3. Sustain peak load: Hold peak load long enough to observe steady-state behavior
  4. Gradual ramp-down: Avoid instant drops to observe system recovery
  5. Set gracefulRampDown: Allow iterations to complete when ramping down
  6. Monitor throughout: Watch metrics during all stages, not just peak
  7. Use realistic patterns: Model actual traffic patterns from your production data

Comparison with Other Executors

FeatureRamping VUsConstant VUsRamping Arrival Rate
VU countVariable over stagesFixedVariable (auto-scaled)
ComplexityMediumLowHigh
Iteration rateVaries with VUsConstant per VUFixed (configured)
Best forRealistic load patternsSteady stateFixed throughput

See Also

Build docs developers (and LLMs) love