skip to content

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

level: middleimportance: should knowfreq 51%

answer

  1. there is always a scenario underneath
  2. the flat one and the ramping one
  3. vus means different things by company
  4. conflicting shortcuts are refused, not ranked

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.

solid answer

~40 s

k6 always runs scenarios, so before the run it converts the shortcut options at the root of the exported `options` object into one scenario named `default`. If `duration` is set, that scenario is `constant-vus`, taking the root `duration` and the root `vus`. If `stages` is set, it is `ramping-vus`, taking the root `stages` — and the root `vus` becomes its `startVUs`, not a fixed count. k6 refuses conflicting shortcuts rather than picking one: `duration` together with `stages` fails with "using `duration` and `stages` options simultaneously is not allowed", and either of them together with an explicit `scenarios` map fails too. The shortcut cannot express `gracefulRampDown`, `startVUs` independently, or more than one scenario.

code

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

export const options = {
  vus: 0,
  stages: [
    { duration: '2m', target: 200 },
    { duration: '5m', target: 200 },
    { duration: '2m', target: 0 },
  ],
};

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

go deeper

for a junior

Know that vus plus duration is the short way to write a constant-vus run and stages is the short way to write a ramping-vus one. Both produce a single scenario called default.

for a middle

Explain the mapping and the refusals: root vus becomes startVUs under stages, and combining duration with stages, or either with an explicit scenarios map, stops the run.

for a senior

Say when to leave the shortcut behind — a second scenario, a non-default gracefulRampDown, or a scenario name worth seeing in results all force the long form.

for a principal

Consider making the long form a team convention. Shortcut scripts hide which executor ran, and an explicit scenarios entry keeps that visible to everyone reviewing a run's results later.

## The root options are a shortcut, not a second engine Every k6 v2 run is made of scenarios, each with an executor, even when the script never writes the word `scenarios`. Before a run starts, k6 rewrites the shortcut options at the root of the exported `options` object into a scenario configuration, and both of the VU-bounded executors have a shortcut that maps onto them. The scenario k6 derives is always a single one named **`default`**. This matters mostly because the same script can be written either way, and reviewers need to know which executor a bare root option actually produced. Three things are true of every derived scenario: - The derivation happens **once, before the run**, so what executes is an ordinary scenario — the run output, the progress line and the per-scenario tagging all behave exactly as they would for a hand-written entry. - The **values are copied unchanged**. A root `duration: '5m'` is the executor's `duration: '5m'`; there is no reinterpretation, only a different place to write it. - The **executor's own validation still applies**. A root `duration: '500ms'` fails on the `constant-vus` one-second floor just as it would inside a scenario entry, and a root `stages` entry missing its `target` fails the same stage validation. ## What k6 derives from which root option | root options set | executor k6 derives | how the values map | |---|---|---| | `duration` (with optional `vus`) | `constant-vus` | `duration` becomes the executor's `duration`, `vus` its `vus` | | `stages` (with optional `vus`) | `ramping-vus` | `stages` becomes the executor's `stages`, `vus` becomes `startVUs` | | `duration` **and** `stages` together | none — k6 refuses to start | *"using `duration` and `stages` options simultaneously is not allowed"* | | either **plus** `scenarios` | none — k6 refuses to start | *"using ... and `scenarios` options simultaneously is not allowed"* | The `vus` row is the one people trip over. At the root, `vus` is a single key that means different things depending on its company: paired with `duration`, it is the fixed count; paired with `stages`, it becomes the ramp's **`startVUs`**, and the peak is whatever the largest `target` is. ## The combinations k6 refuses 1. **`duration` with `stages`.** These describe two different executors, so k6 will not guess. The run ends before the first request, with the invalid-config exit code and the message quoted above. 2. **A shortcut with an explicit `scenarios` map.** If you have written scenarios out in full, the root shortcut is redundant and ambiguous, so k6 rejects the combination rather than merging them. 3. **`stages: []`.** An explicitly empty array is not the ramping shortcut. k6 warns that stages was set to an empty value and falls back to running the script once with one VU. ## What the root form cannot say The shortcut is a convenience, and it is strictly less expressive than writing the scenario out: - There is **no root `startVUs`** — root `vus` is the only way in, so `startVUs: 0` with a nonzero fixed count elsewhere cannot be expressed. - There is **no root `gracefulRampDown`** — a shortcut-derived `ramping-vus` scenario always takes the `30s` default. - There is **no root `startTime`, `exec`, `env` or `tags`** for the derived scenario, because those are per-scenario keys. - You get **exactly one scenario**, so two profiles running side by side is not expressible as a shortcut. ## When the shortcut is enough For a flat run — a fixed VU count for a fixed time — `vus` plus `duration` at the root is two lines and says everything. `stages` at the root is fine too when the default `gracefulRampDown` suits you and the run is a single profile. The moment you need a second scenario, a non-default wind-down, or a named entry someone can point at in review, move to the full form: put an entry in `scenarios`, set `executor` to `'constant-vus'` or `'ramping-vus'` explicitly, and the same keys carry over unchanged. ## Reading someone else's script When you open an unfamiliar k6 script, look at the root options first and ask which executor it implies. A `stages` array with no `scenarios` map is a `ramping-vus` run whose start count is the root `vus` or, if that is absent, the default `1` — which is a genuinely easy detail to miss when the first stage looks like it should be starting from nothing.

  • What does k6 name the scenario it derives from a root shortcut?
    `default`. Whichever shortcut you use, k6 produces exactly one scenario under that key, which is why shortcut output and per-scenario tags both show `default` as the scenario name. Writing the scenario out by hand lets you name it something the results can be read by.
  • Can you set gracefulRampDown through k6's root stages shortcut?
    No. There is no root option for it, so a shortcut-derived `ramping-vus` scenario always uses the `30s` default. To choose a different window you have to write the scenario out in the `scenarios` map with `executor: 'ramping-vus'` and set the key on the entry.

saying these in an interview costs you the question

  • Thinks root options bypass scenarios and executors entirely
  • Expects stages to win silently when duration is also set
  • Reads root vus as the peak of a stages run rather than startVUs
  • Believes root shortcuts merge with an explicit scenarios map
  • Assumes gracefulRampDown can be set at the root of options