skip to content

For a k6 suite in CI, which settings belong in the exported `options` object rather than on the command line?

level: principalimportance: should knowfreq 40%

answer

  1. the tool decides part of it
  2. no flag for thresholds or scenarios
  3. flags carry size, not meaning
  4. commit the invocation as well

basics

~20 s

k6 settles part of it: thresholds, scenarios and cloud have no k6 run flag, so they can only live in the script or a --config JSON file. The flags carry the size knobs - vus, duration, stage, tag.

solid answer

~40 s

Some of the decision is made for you. In k6 v2 there is no `--thresholds` and no `--scenarios` flag, so a suite's pass rules and its workload shape can only come from the script's exported `options` or from a JSON file passed with `--config`. The flags cover the size knobs - `--vus`, `--duration`, `--stage`, `--iterations`, `--rps`, `--tag` - and that is the right split to keep: everything that defines *what the test is* belongs in the committed object, and only what genuinely varies per invocation belongs on the command line. Then commit the invocation too, in the CI job or a Makefile target, because a run's meaning otherwise depends on a string nobody reviewed.

code

javascript · 10 lines
javascript
// what the test is - committed, reviewed, unchanged per run
export const options = {
  thresholds: { http_req_duration: ['p(95)<500'] },
  discardResponseBodies: true,
  tags: { suite: 'checkout-smoke' },
  vus: 20,
  duration: '10m',
};

export default function () {}

go deeper

for a junior

Learn the hard constraint first: thresholds and scenarios have no command-line flag, so they have to be written in the script. The flags cover size settings like --vus and --duration.

for a middle

Explain the split in terms of what changes. Anything that defines what the test measures belongs in the exported object; anything that describes this particular run belongs on the invocation.

for a senior

Show that you commit the invocation too, and that you know the failure modes differ - a bad flag is rejected by the parser, a bad key in the exported object only warns and runs anyway.

for a principal

Own the standard for the team: which keys are allowed on a command line at all, where the invocation lives, and how a run proves what it was configured with. The goal is that the repository alone explains any result.

## Start from what k6 does not let you choose Before this becomes a matter of taste, k6 removes part of the decision. Three root keys have no command-line flag at all in k6 v2: - **`thresholds`** - no flag. - **`scenarios`** - no flag. - **`cloud`** - no flag. They can be set in the script's exported `options` object, or in a JSON file passed with `--config`, and nowhere else. So the pass rules a run is judged against and the shape of the workload it applies are, by construction, file-resident configuration. You cannot improvise them at the prompt even if you want to. ## What the flags are actually for Everything the flags cover is a **size or labelling knob** on a workload that has already been described: | lever | flag | good reason to use it | |---|---|---| | how many VUs | `--vus`, `-u` | shrink a committed script for a quick run | | how long | `--duration`, `-d` | cut a long run short while debugging | | a ramp step | `--stage`, `-s` (repeatable) | try a shape before committing it | | how many iterations | `--iterations`, `-i` | one-off exploratory run | | request-rate cap | `--rps` | protect a shared environment | | run identification | `--tag NAME=VALUE` | stamp the run's metrics with a build number | Note the shape of that list: none of them changes what the test *asserts* or what it *does*. That is the natural border, and k6's own flag surface already draws it. ## The `--config` middle ground `k6 run --config options.json script.js` reads the same keys from a JSON file, including `thresholds` and `scenarios`. It is worth knowing because it lets configuration be a reviewable file without living inside the script - useful when one script is run under several named configurations, or when the configuration is generated. The cost is a second file to keep in step with the script, and a script that no longer tells you how it runs. Reach for it when the same script genuinely has several committed shapes; otherwise keep the object in the script. ## A split that holds up 1. **In the exported `options` object**: `thresholds`, `scenarios` (or the shorthand that stands in for it), `tags` that identify the *test* rather than the run, and the protocol-level settings - `discardResponseBodies`, `maxRedirects`, `insecureSkipTLSVerify`, `userAgent`. These describe what the test is, and a change to any of them should appear in a pull request. 2. **On the invocation**: the per-environment size and the per-run labels - `--vus`, `--duration`, `--tag build=$CI_BUILD`. These are properties of *this* run, not of the test. 3. **Committed either way**: put the invocation in the CI job definition or a Makefile target, not in a wiki page. A flag that lives only in someone's shell history is configuration nobody can review, and it is the half of the run's meaning that is missing from the repository. ## The tension worth naming in an interview There is a real pull in both directions, and k6 sharpens it in a way worth saying out loud. - Push everything into the script and a smoke run becomes a code change, or a pile of `__ENV` branching inside `options` that is harder to read than the flags it replaced. - Push too much onto the command line and a green run proves nothing on its own: the results say a run passed, and the repository cannot tell you what it ran. The k6-specific tiebreaker is how the two channels fail. A misspelled root key inside the exported `options` object only produces a warning, and the run continues with the setting at its default - so a threshold that silently never registered still exits 0. A misspelled *flag* is rejected by the argument parser and the command fails immediately. That cuts both ways, and it is the honest answer: the command line is the more strictly validated channel, but the script is the reviewable one. So keep the meaning of the test in the script where a human checks it in review, keep the run's size on a command line that a parser checks, and close the gap on the script side deliberately - by failing the pipeline on k6's unknown-fields warning rather than trusting that the object says what you think it says.

  • If `thresholds` cannot be a k6 flag, how do you vary them per environment?
    Two supported routes: build the object in the script from `__ENV` values passed with `--env`, or keep one JSON file per environment and select it with `--config`. Both keep the rules in a reviewable file. What you cannot do is pass a threshold on the command line - k6 v2 exposes no flag for it.
  • Why commit the `k6 run` invocation as well as the script?
    Because the flags carry half the run's configuration. A results artifact that says a run passed is only interpretable alongside the size it ran at, and if that size lived in a shell history nobody can reconstruct it. Putting the command in the CI job file or a Makefile target makes the whole configuration reviewable.

saying these in an interview costs you the question

  • Assumes every k6 option can be passed as a flag
  • Says thresholds can be overridden with a k6 run flag
  • Leaves the k6 invocation only in shell history
  • Treats a green k6 exit code as self-describing
  • Puts per-run build labels inside the committed options object