skip to content

Bounded Users

The four k6 executors where a user must finish one iteration before starting the next, so a slower system lowers the achieved rate. Asked because that self-throttling hides overload behaviour.

on this pageshow

explore

questions

8

Which k6 scenario do the root vus and iterations options build, and what does duration do there?

level: juniorimportance: must knowfreq 68%

answer

  1. the shortcut has a target executor
  2. total across VUs, not per VU
  3. duration lands on a different key
  4. derived scenario is named default
  5. shared-iterations is what you get

basics

~20 s

k6 turns root vus and iterations into a single scenario named default that uses the shared-iterations executor, so iterations is the total across all VUs. A root duration set alongside them becomes that scenario's maxDuration.

solid answer

~40 s

Setting `vus` and `iterations` at the **root** of k6's exported `options` is a shortcut: k6 derives one scenario called `default` running the **`shared-iterations`** executor. So `iterations` is the total across all VUs, not a per-VU count — the `-i` flag's help even says *script total iteration limit (among all VUs)*. If you also set a root `duration`, it is not a competing run length; k6 copies it into the derived scenario's `maxDuration`, replacing the `'10m'` default. Combining root `iterations` with `stages` or with an explicit `scenarios` map is rejected outright rather than merged. With no execution options at all, k6 falls back to a `per-vu-iterations` scenario of one VU and one iteration.

code

javascript · 18 lines
javascript
// Shortcut form: 500 CSV rows, 20 VUs, 30 minute ceiling.
export const options = {
  vus: 20,
  iterations: 500,
  duration: '30m',
};

// Exactly what k6 derives from it:
export const equivalent = {
  scenarios: {
    default: {
      executor: 'shared-iterations',
      vus: 20,
      iterations: 500,
      maxDuration: '30m',
    },
  },
};

go deeper

for a junior

Know the mapping cold: root vus plus iterations equals one shared-iterations scenario called default, and iterations is the total across all VUs. That single fact answers most screening questions on k6 options.

for a middle

Be able to write out the longhand scenario the shortcut expands to, and explain that a root duration lands on maxDuration rather than replacing the budget. Know that the no-options fallback is per-vu-iterations instead.

for a senior

Say when you stop using the shortcut: more than one scenario, a startTime, a per-scenario exec, or per-VU semantics. Note that adding a scenarios map is an error while root iterations is still set, so it is a move not an addition.

for a principal

Treat the shortcut as fine for a throwaway or a smoke run and a liability in a shared suite, where the derived executor is invisible in review. Standardising on explicit scenarios makes the executor a reviewable choice.

## The shortcut and what it derives k6 lets you skip the `scenarios` map entirely for simple runs. Setting the **root** options `vus` and `iterations` — at the top level of the exported `options` object, not inside a scenario — makes k6 build a single scenario for you. That scenario is named `default` and it uses the **`shared-iterations`** executor, so `iterations` is the **total** across all VUs, not a per-VU figure. The `--iterations`/`-i` flag says as much in its own help text: *script total iteration limit (among all VUs)*. ```javascript export const options = { vus: 5, iterations: 10 }; ``` is the same run as: ```javascript export const options = { scenarios: { default: { executor: 'shared-iterations', vus: 5, iterations: 10 }, }, }; ``` ## What `duration` does when `iterations` is also set This is the part people get wrong. A root `duration` alongside a root `iterations` does **not** turn the run into a timed one and does not fight with the budget — it is copied into the derived scenario's `maxDuration`, replacing the `'10m'` default. So `k6 run -u 1 -i 6 -d 10s script.js` is a six-iteration `shared-iterations` run with a ten-second ceiling. ## The full derivation order k6 checks the root options in a fixed order and the first match wins: 1. `iterations` set → a `shared-iterations` scenario, taking `vus` and using `duration` as `maxDuration`. 2. otherwise `duration` set → a `constant-vus` scenario. 3. otherwise `stages` set → a `ramping-vus` scenario. 4. otherwise `vus` set alone → a `shared-iterations` scenario where **`iterations` is set equal to `vus`**, so `k6 run --vus 8 script.js` runs 8 iterations across 8 VUs and exits. 5. otherwise `scenarios` set → used as written, nothing is derived. 6. otherwise nothing at all → a `per-vu-iterations` scenario with 1 VU and 1 iteration. That is why a bare `k6 run script.js` executes the default function exactly once. Note the asymmetry between branch 4 and branch 6: the *zero-options* default is `per-vu-iterations`, while every iteration **shortcut** lands on `shared-iterations`. Because the branches are ordered rather than combined, `iterations` also beats a `duration` you set for a different reason — there is no way to express "run for ten minutes *and* stop at 500 iterations" as two independent limits through the shortcut, since the second one is reinterpreted as the ceiling of the first. ## Conflicts k6 refuses - `iterations` together with `stages` is rejected: *using `iterations` and `stages` options simultaneously is not allowed*. - `iterations` together with `scenarios` is rejected the same way. The shortcut and the explicit map are mutually exclusive, not merged, so there is no partial-override behaviour to reason about. - The message names the two options directly, which is usually enough to locate the problem: the pair may have arrived from two different places, for instance `iterations` from an environment variable and `scenarios` from the script. - The bare-`vus` branch behaves differently: with a script-defined `scenarios` map it does **not** fail, it logs a warning that `vus=N` overrides the scenarios configuration and the derived shortcut wins. ## Where you can set it The same shortcut is available in all three places k6 reads configuration from, with the same meaning: | form | example | |---|---| | exported `options` | `export const options = { vus: 5, iterations: 10 };` | | environment | `K6_VUS=5 K6_ITERATIONS=10 k6 run script.js` | | CLI flags | `k6 run -u 5 -i 10 script.js` | k6's own `k6 run` help lists `# Run 5 VUs, splitting 10 iterations between them.` next to `-u 5 -i 10` — "splitting" being the giveaway that this is the shared, not the per-VU, executor. ## When to stop using it: the 500-row CSV pass For a 500-row data-driven pass the shortcut is genuinely enough: `export const options = { vus: 20, iterations: 500 };` runs each row once. Write the scenario out longhand as soon as you need something the shortcut cannot express: - a `maxDuration` that is not simply the root `duration`, or a `startTime`; - `per-vu-iterations` semantics, which no root shortcut can produce; - more than one scenario, or a named scenario other than `default`; - a scenario-level `exec` pointing at a function other than the default export; - scenario-level `env` or `tags` that should apply to this workload and not the whole run. Because reaching for `scenarios` and keeping the root `iterations` is an error rather than a merge, that migration is an edit, not an addition: move `vus` and `iterations` into the scenario entry and delete them from the root.

  • What does k6 run script.js do when the script sets no execution options at all?
    It derives a single scenario named `default` using the `per-vu-iterations` executor with `vus: 1` and `iterations: 1`, so the default function runs exactly once. Note the asymmetry: the no-options fallback is `per-vu-iterations`, while every iteration shortcut lands on `shared-iterations`.
  • In k6, what happens if you set only a root vus and no iterations, duration or stages?
    k6 derives a `shared-iterations` scenario and sets `iterations` equal to `vus`, so `k6 run --vus 8 script.js` runs 8 iterations across 8 VUs and exits. It is rarely what the author meant, which is why the flag is usually paired with `-i` or `-d`.
  • Can a k6 script keep root iterations and add a scenarios map for a second scenario?
    No. k6 rejects the combination with *using `iterations` and `scenarios` options simultaneously is not allowed* — the shortcut and the explicit map are mutually exclusive, never merged. Moving to `scenarios` means deleting the root `vus` and `iterations` in the same edit.

saying these in an interview costs you the question

  • Says root vus plus iterations gives each VU that many iterations
  • Thinks a root duration overrides the iteration budget and times the run
  • Expects root iterations and a scenarios map to be merged together
  • Assumes the shortcut derives per-vu-iterations rather than shared-iterations
  • Believes the derived scenario has no maxDuration ceiling at all
open as a page

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

level: juniorimportance: must knowfreq 82%

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.

open as a page

In k6, how do the shared-iterations and per-vu-iterations executors differ in spending an iteration budget?

level: middleimportance: must knowfreq 76%

basics

~20 s

k6's shared-iterations executor treats iterations as one total pool that any free VU pulls from, so per-VU counts come out uneven. per-vu-iterations gives every VU that exact count, making the scenario total vus times iterations.

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

Why does k6 reject a shared-iterations scenario configured with vus: 20 and iterations: 6?

level: middleimportance: should knowfreq 44%

basics

~10 s

shared-iterations requires iterations to be at least vus, so a pool of 6 leaves 14 of the 20 VUs nothing to claim. k6 fails configuration validation before the run starts and names both numbers.

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

In a k6 shared-iterations scenario, what happens to iterations still unrun when maxDuration expires?

level: seniorimportance: should knowfreq 57%

basics

~20 s

They are never run. k6 stops starting new iterations at maxDuration, which defaults to 10 minutes, lets in-flight ones finish within gracefulStop, and counts the unstarted remainder on the dropped_iterations metric. The run itself does not fail.

open as a page