skip to content

Iteration Budget Runs

Running a counted budget of iterations rather than a clock: one pool shared across users, or an exact count each. Asked because picking the wrong one makes a run's work volume unrepeatable.

on this pageshow

explore

questions

4

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

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

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