Which k6 option layers can supply a scenarios map, and which layers cannot reach it at all?
answer
- not every option has three doors
- check the Env and CLI columns
- the map has no variable form
- script or config file only
basics
~20 sA k6 scenarios map can only come from the script's exported options or a --config JSON file. No K6_SCENARIOS variable or CLI flag exists, so the top two layers can only discard a map, never supply one.
solid answer
~40 sIn k6 v2 the workload options are not symmetric across the five layers. `vus`, `duration`, `iterations` and `stages` each have all three forms — `K6_VUS` / `--vus`, `K6_DURATION` / `--duration`, `K6_ITERATIONS` / `--iterations`, `K6_STAGES` / `--stage` — plus the script and config file. `scenarios` has only the script and the config file: there is no `K6_SCENARIOS` and no `--scenarios`. Since those four keys form one execution group that a higher layer clears wholesale, the environment and CLI layers can destroy a scenarios map but can never replace it with a different one. Whatever they do, the run ends up with the single derived default scenario.
code
bash · 9 lines# These all work — each key has an env form and a flag form.
K6_VUS=10 K6_DURATION=30s k6 run script.js
K6_STAGES="5s:10,5m:20,10s:5" k6 run script.js
k6 run --vus 10 --duration 30s script.js
k6 run --stage 5s:10 --stage 5m:20 script.js
# There is no K6_SCENARIOS and no --scenarios flag in k6 v2.
# The only ways in are the script or a config file:
k6 run -c workload.json script.jsgo deeper
Know that scenarios is written in the script's options object, and that there is no command-line flag or environment variable for it. Also note the stage flag is singular, --stage, repeated once per stage.
Explain the asymmetry: vus, duration, iterations and stages each have env and flag forms, scenarios has neither, so the top two layers can only clear a map rather than replace it.
Trace a surprising run by reading exec.test.options.scenarios and by scanning the pipeline for any K6_DURATION, K6_STAGES or --stage that collapsed a declared map to one scenario.
Weigh the shape of a suite against this limit: if per-environment workloads matter, decide between per-environment config files and a script that assembles its own scenarios object.
## Not every k6 option has an entry in every layer k6 resolves options through five layers — defaults, the `--config` file, the script's exported `options`, `K6_*` environment variables, and CLI flags — but a given option only participates in the layers that have a way to *name* it. The workload keys are the clearest split. ## What each execution option can be set from | Option | Environment variable | CLI flag | Script / config file | |---|---|---|---| | `vus` | `K6_VUS` | `--vus`, `-u` | yes | | `duration` | `K6_DURATION` | `--duration`, `-d` | yes | | `iterations` | `K6_ITERATIONS` | `--iterations`, `-i` | yes | | `stages` | `K6_STAGES` | `--stage`, `-s` | yes | | `scenarios` | none | none | yes | `K6_STAGES` takes the same compact form as the flag, one `duration:target` pair per comma: `K6_STAGES="5s:10,5m:20,10s:5"`. Note the flag is singular — `--stage`, repeated once per stage — not `--stages`. Each of the first four rows also has a short flag form: `-u` for `--vus`, `-d` for `--duration`, `-i` for `--iterations`, `-s` for `--stage`. The pattern behind the table is that a layer can only carry an option it has a **syntax** for. The environment layer carries strings, so it can express a number, a duration, or a compact comma-separated list. The flag layer carries strings too, with the same limitation. Both fall down on a nested object, which is exactly what a scenarios map is: named keys, each holding an executor configuration with its own sub-keys. ## `scenarios` is script-and-file only There is no `K6_SCENARIOS` variable and no `--scenarios` flag in k6 v2. The scenarios map is a nested object of executor configurations, and neither the environment-variable reader nor the flag set has a representation for it. The only two ways in are: 1. `export const options = { scenarios: { ... } }` in the test script. 2. A `scenarios` key in the JSON file that `--config` / `-c` points at (or the default `config.json`). `ext` behaves the same way — visible in the script and the config file, invisible to the top two layers. ## The consequence that matters for precedence Because `duration`, `iterations`, `stages` and `scenarios` form one execution group, and because a higher layer setting **any** of the four clears **all four** below it, the asymmetry has a sharp effect: - The environment and CLI layers **can destroy** a scenarios map — `K6_DURATION=1m` or `--stage 30s:10` is enough to clear it entirely. - The environment and CLI layers **cannot replace** it with a different map, because they have no syntax for one. - So whatever those layers do to a scenarios map, the result is always the single derived default scenario, never a second multi-scenario workload. That is the whole shape of the trap: the two layers that outrank the script are the two layers that cannot express the script's richest workload description. ## Practical consequences - A CI job that wants to vary the workload per environment must vary something the script reads, or ship a different config file — it cannot inject a scenarios map through the environment. - If your suite is built on `scenarios`, any `K6_DURATION`, `K6_ITERATIONS`, `K6_STAGES`, `--duration`, `--iterations` or `--stage` present in the pipeline silently collapses it to one scenario. - Reading the k6 options reference is the fastest check: each option's table has an **Env** column and a **CLI** column, and `N/A` in both means the script or the config file is the only way in. - A wrapper that "just adds `--vus`" for a smoke run is enough on its own: with no other execution key present, k6 discards the map and derives a `shared-iterations` scenario instead. - Two different config files behind `-c` are a legitimate way to vary a workload per environment, because the file layer *can* hold a map — unlike the two layers above it. - Building the map in the script from values the script itself reads is the other legitimate route, since the object is assembled before `options` is exported. ## Confirming what a run resolved From inside the test, `exec.test.options` (from `k6/execution`) exposes the consolidated, derived options, so `exec.test.options.scenarios` shows the map k6 actually built after every layer was applied. If the script declares four scenarios and that object holds a single `default` entry, a higher layer set one of the group keys.
- How can a k6 pipeline vary the workload per environment if scenarios has no variable?Either point `--config` / `-c` at a different JSON file per environment, or have the script build its `scenarios` object itself before exporting `options`. What it cannot do is inject a map through `K6_*`, because no such variable exists — and reaching for `K6_DURATION` instead collapses the map to one derived scenario.
- Is scenarios the only k6 root option missing from the environment and CLI layers?No. `ext` is another key visible only in the script and the config file. Several others are partial rather than absent — `executionSegment`, for instance, has a `--execution-segment` flag but no environment-variable form. The options reference marks each case with `N/A` in its Env and CLI columns.
saying these in an interview costs you the question
- Claims K6_SCENARIOS sets the scenarios map
- Expects a --scenarios flag to exist on k6 run
- Uses --stages plural instead of the singular --stage
- Thinks every k6 option has an env and a flag form
- Believes an env variable can add a second scenario