skip to content

Fixed and Ramping Counts

Holding a set number of virtual users for a duration, or walking that number through stages. Interviewers probe it because the ramp and its wind-down decide which slice of a run is measurable.

on this pageshow

explore

questions

4

In k6, how do the constant-vus and ramping-vus executors differ, and what options does each take?

level: juniorimportance: must knowfreq 82%

answer

  1. fixed headcount versus a schedule
  2. each executor owns its own key pair
  3. the ramp needs a starting value
  4. stage entries carry duration and target

basics

~20 s

k6's constant-vus executor holds a fixed number of VUs for a set time and takes vus plus duration. ramping-vus moves the count through a schedule and takes startVUs, stages and gracefulRampDown. Neither accepts the other's keys.

solid answer

~40 s

Both are VU-bounded executors in k6 v2: you say how many virtual users should be looping the exported `default` function, and each VU starts its next iteration as soon as the previous one returns. `constant-vus` takes `vus` (default `1`) and a required `duration` that must be at least `1s`; it plans every VU at time offset 0 and never changes the count. `ramping-vus` takes `startVUs` (default `1`), a required `stages` array of `{ duration, target }` entries, and `gracefulRampDown` (default `30s`), which no other executor has. Both also accept the shared per-scenario keys such as `startTime` and `exec`. Because k6 parses a scenario entry with unknown fields disallowed, putting `stages` in a `constant-vus` entry — or `duration` in a `ramping-vus` one — fails configuration instead of being ignored.

code

javascript · 15 lines
javascript
import http from 'k6/http';

export const options = {
  scenarios: {
    steady: {
      executor: 'constant-vus',
      vus: 200,
      duration: '5m',
    },
  },
};

export default function () {
  http.get('https://quickpizza.grafana.com/');
}

go deeper

for a junior

Memorise the two key pairs: constant-vus is vus plus duration, ramping-vus is startVUs plus stages. Being able to write either scenario entry from memory is the whole ask at this level.

for a middle

Explain the defaults and the validation: vus and startVUs both default to 1, duration is required with a one-second floor, and stages needs at least one entry with both duration and target present.

for a senior

Show that you know a mistyped key inside a scenario entry stops k6 before the run rather than being ignored, and that the same typo at the root of options only produces a warning.

for a principal

Talk about which shape a team should standardise on. A flat constant-vus scenario is two numbers anyone can review; a stages array encodes a whole profile and needs a convention so scenarios stay comparable across services.

## Two ways to bound a k6 scenario by virtual users k6 v2 ships six executors, and two of them size a scenario by **virtual users (VUs)** rather than by an iteration budget or an arrival rate: `constant-vus` and `ramping-vus`. A VU in k6 is a JavaScript runtime with its own state that calls the script's exported `default` function in a loop; one call is one **iteration**, and k6 starts that VU's next one as soon as the previous returns. Both work that way. What separates them is whether the number of looping VUs is a single constant or a schedule. Both are written as one entry in the `scenarios` map with `executor` set to the executor's string id, and both accept the shared per-scenario keys every k6 executor has — `startTime`, `gracefulStop`, `env`, `exec`, `tags`, `options`. Everything below is what is unique to each of the two. ## `constant-vus` takes `vus` and `duration` - **`vus`** — how many VUs loop concurrently. Defaults to **`1`**, and k6 rejects a value of 0 or less. - **`duration`** — how long they loop. **Required**, and checked against a **one-second floor**: a `duration` of `500ms` is rejected with *"the duration must be at least 1s"*, and leaving it out is rejected with *"the duration is unspecified"*. k6 plans the whole `vus` count at time offset 0, so every VU is initialized before the scenario starts, and the count never moves while it runs. There is no third key: `constant-vus` is genuinely two numbers. ## `ramping-vus` takes `startVUs`, `stages` and `gracefulRampDown` - **`startVUs`** — the VU count at the scenario's first moment. Defaults to **`1`**; `0` is legal, a negative value is not. - **`stages`** — **required**, an array of `{ duration, target }` objects. Each `target` is the VU count to reach at the **end** of that stage, and k6 interpolates linearly from whatever count is running when the stage begins. Stage durations run consecutively, so their sum is the scenario's scheduled length. - **`gracefulRampDown`** — how long a VU that is being dropped may keep running the iteration it is already in. Defaults to **`30s`**. No other k6 executor has this key. Its validation is just as strict: `stages` needs at least one entry (*"at least one stage has to be specified"*), every entry needs both fields (*"stage N doesn't have a duration"*, *"stage N doesn't have a target"*), and either `startVUs` or one of the targets must be greater than 0. ## The two, side by side | | `constant-vus` | `ramping-vus` | |---|---|---| | where the VU count comes from | `vus`, default `1` | `startVUs`, default `1`, then each stage's `target` | | how long the scenario runs | `duration`, required, at least `1s` | the sum of every `stages` entry's `duration` | | wind-down of dropped VUs | not applicable — the count never falls mid-scenario | `gracefulRampDown`, default `30s` | | shape of the VU curve | flat | piecewise linear | | root-option shortcut | `vus` plus `duration` | `stages` | ## Neither executor tolerates the other's keys k6 decodes a `scenarios` entry with unknown fields disallowed, so a key the chosen executor does not define is a **fatal configuration error**: k6 stops before the first request with the invalid-config exit code rather than warning and carrying on. In practice: 1. `stages` inside a `constant-vus` entry fails — the fixed executor has no ramp of any kind. 2. `duration` inside a `ramping-vus` entry fails — its length is the sum of the stage durations and cannot be capped by a separate key. 3. `vus` inside a `ramping-vus` entry fails — the starting count is spelled `startVUs`. 4. `gracefulRampDown` inside a `constant-vus` entry fails — nothing ramps down there. That strictness is deliberate: a mistyped scenario key stops the run instead of silently changing its shape. Note the asymmetry, because it catches people out — an unknown key at the **root** of the exported `options` object only logs *"There were unknown fields in the options exported in the script"* and the run continues; the fatal behaviour is specific to keys inside a scenario entry. ## Picking one Use `constant-vus` when the VU count is one number for the whole scenario — it is two keys, and the count you configure is the count that runs from the first second to the last. Use `ramping-vus` when the count has to move during the scenario, in either direction, or more than once. Because both live in the same `scenarios` map with the same shared keys, moving from one to the other is a matter of swapping the `executor` string and its two or three own keys.

  • What is the shortest duration a k6 constant-vus scenario will accept?
    One second. k6 validates the executor's `duration` against a `1s` floor, so `duration: '500ms'` fails with "the duration must be at least 1s", and omitting it fails with "the duration is unspecified". The floor is on the executor's own `duration` only — a `ramping-vus` stage is allowed `duration: '0s'`, which k6 treats as an instantaneous jump to that stage's target.
  • When does k6 initialize the VUs for a constant-vus scenario?
    All of them up front. `constant-vus` plans its full `vus` count at time offset 0, so k6 initializes every VU before the scenario's first iteration and holds them for the whole `duration`. Nothing is added mid-run, because the count is fixed for the life of the scenario.

saying these in an interview costs you the question

  • Says a constant-vus entry can carry a stages array
  • Thinks ramping-vus takes a duration key of its own
  • Calls vus the peak count for ramping-vus instead of startVUs
  • Assumes an unknown key in a scenario entry is silently ignored
  • Believes gracefulRampDown applies to constant-vus as well
open as a page

In a k6 ramping-vus scenario, what do the duration and target of one stages entry mean?

level: middleimportance: must knowfreq 68%

basics

~20 s

In k6's ramping-vus executor, a stages entry's target is the VU count reached at the end of the stage, and duration is how long the linear move from the current count takes. Repeating the previous target holds that level.

open as a page

In a k6 ramping-vus run, why are iterations interrupted during ramp-down stages, and what controls it?

level: seniorimportance: must knowfreq 58%

basics

~20 s

k6's ramping-vus executor gives every VU it drops a gracefulRampDown window, 30s by default, to finish the iteration it is already in. A VU still working when the window closes is hard stopped mid-iteration. Setting gracefulRampDown to 0s interrupts immediately.

open as a page

Which executors does k6 derive from the root vus, duration and stages options?

level: middleimportance: should knowfreq 51%

basics

~10 s

k6 rewrites root options into one scenario named default: duration, with optional vus, becomes a constant-vus scenario, and stages, with vus supplying startVUs, becomes ramping-vus. Setting duration and stages together is rejected outright.

open as a page