In k6, what is the exported `options` object and how does it configure a test run?
answer
- the script is its own config file
- a named export, read at init
- export const options = { ... }
- root keys: vus, duration, stages, scenarios
basics
~20 sk6 has no separate configuration file format. A k6 script exports an object named options, k6 reads it once at init, and its root keys - vus, duration, stages, scenarios, thresholds, tags - become the run configuration.
solid answer
~40 sA k6 script configures itself. You add a named export called `options` - idiomatically `export const options = { ... }` - and k6 evaluates the script once before any virtual user exists, then decodes that object into the run configuration. Root keys include `vus`, `duration`, `stages`, `scenarios`, `thresholds`, `tags`, `rps`, `discardResponseBodies` and `summaryTrendStats`. Because it is ordinary JavaScript sitting in the script file, load settings are reviewed and version-controlled alongside the code they exercise. Many of the same settings also have a `k6 run` flag - `--vus`/`-u`, `--duration`/`-d`, `--stage`/`-s`, `--tag` - so a committed script can be run once at a different size without editing the file. A few keys, notably `thresholds` and `scenarios`, have no flag at all.
code
javascript · 14 linesimport http from 'k6/http';
export const options = {
vus: 10,
duration: '5m',
rps: 200,
discardResponseBodies: true,
tags: { service: 'checkout' },
thresholds: { http_req_duration: ['p(95)<500'] },
};
export default function () {
http.get('https://quickpizza.grafana.com/');
}go deeper
Be able to write the export from memory and name what vus, duration and stages do. Knowing that the settings live in the script itself, not in a separate config file, is most of the answer at this level.
Explain the mechanics: k6 evaluates the script once before any VU exists, serialises the exported value and decodes it, so the object must be JSON-shaped data and is read once per run rather than per VU.
Show you know which keys have no command-line flag, and why that shapes how a team runs k6 in CI. Being able to say that thresholds and scenarios must be committed is the production-relevant half.
The tradeoff is what the committed object should fix and what the invocation is allowed to vary. Fixing too much makes one-off runs need code changes; fixing too little makes runs unreproducible from the repository alone.
## The script is the configuration k6 does not read a separate settings file before it reads your test. A k6 test is a JavaScript module, and one of the things that module exports is an object named `options`. Everything the run needs to know that is not test logic - how many virtual users, for how long, what to tag the metrics with, what to assert - is a key in that object, in the same file, in the same commit as the requests it configures. The name is the contract. k6 looks up the module export called `options`: ```javascript export const options = { vus: 10, duration: '30s' }; ``` `export let options = { ... }` behaves identically, and in base compatibility mode `module.exports.options = { ... }` does too. `const` is convention, not a requirement. ## When k6 reads it k6 evaluates the script once, in a throwaway init runtime, **before any virtual user exists**. It takes the exported value, serialises it to JSON, and decodes that JSON into its own internal options structure. Three consequences follow, and they explain most surprises: 1. The object may be **computed** rather than written as a literal, because it is ordinary JavaScript that runs before the decode - the value just has to exist by the time the module finishes evaluating. 2. It must reduce to **JSON-shaped data**. A value that JSON cannot represent is not configuration k6 can read, so the object holds plain objects, arrays, strings, numbers and booleans and nothing more. 3. It is read **once for the whole run**, not per VU and not per iteration. A computed `options` therefore cannot vary between virtual users: every VU is created after the decode has already happened. ## The root keys The object is flat: keys sit at its top level, in camelCase. The ones you meet first: - **`vus`** - how many virtual users to run concurrently. Defaults to 1. - **`duration`** - how long the run lasts, as a duration string such as `'30s'` or `'5m'`. - **`stages`** - an array of `{ duration, target }` steps, for a run whose VU count changes over time. - **`scenarios`** - a map of named, independently configured workloads, for anything the three keys above cannot express. - **`thresholds`** - the pass rules the run is judged against. - **`tags`** - an object of key/value pairs attached to every metric sample the run emits. - **`rps`** - a cap on HTTP requests per second shared across all VUs; `0`, the default, means unlimited. - **`discardResponseBodies`** - `false` by default; set it to `true` and k6 reads response bodies without keeping them. - **`summaryTrendStats`** - which statistics the end-of-test summary prints for timing metrics. There are many more - `httpDebug`, `insecureSkipTLSVerify`, `maxRedirects`, `noConnectionReuse`, `setupTimeout`, `userAgent`, `systemTags` and others - but the list above covers almost every real script. ## The same setting, from the command line Most root keys have a matching `k6 run` flag, and the flag name is the kebab-case form of the camelCase key. Two of them are deliberately singular, because the flag is repeatable while the key takes a collection: | `options` key | `k6 run` flag | note | |---|---|---| | `vus` | `--vus`, `-u` | default `1` | | `duration` | `--duration`, `-d` | | | `iterations` | `--iterations`, `-i` | | | `stages` (array) | `--stage`, `-s` | flag is singular and repeatable | | `rps` | `--rps` | `0` means unlimited | | `tags` (object) | `--tag NAME=VALUE` | flag is singular and repeatable | | `discardResponseBodies` | `--discard-response-bodies` | | | `summaryTrendStats` | `--summary-trend-stats` | | | `thresholds` | *(no flag)* | script or a `--config` JSON file only | | `scenarios` | *(no flag)* | script or a `--config` JSON file only | So a script that declares `vus: 10, duration: '5m'` can still be run once as `k6 run --vus 1 --duration 10s script.js` for a smoke check, without touching the file. What you cannot do from the command line is add a threshold or a scenario: those two keys have no flag in k6 v2, which is a good reason to think of the exported object as the primary home for configuration and the flags as the escape hatch. ## What is different in k6 v2 Cloud settings live under the root **`cloud`** key. The older `options.ext.loadimpact` block was removed in k6 v2.0.0 and is no longer read, so a script carrying one is silently configuring nothing. Feature flags are the one documented exception to "options configures the run": a `features` key in the exported object is not honoured, and k6 logs a warning telling you to use `--features`, `K6_FEATURES` or the JSON config file instead.
- Does k6 require `export const options`, or will another export form work?k6 looks up the module export whose name is `options`; the declaration form is yours. `export const options = { ... }` is the convention, `export let options = { ... }` is equivalent, and in base compatibility mode `module.exports.options = { ... }` works too. Renaming the export is what breaks it - k6 will simply find no options and run with defaults.
- Which k6 root options keys cannot be set from a `k6 run` flag?`thresholds`, `scenarios` and `cloud` have no command-line flag in k6 v2. They can only come from the script's exported `options` object or from a JSON file passed with `--config`. That is why a k6 suite's pass rules and its workload shape effectively have to be committed rather than improvised at the prompt.
- Where do cloud settings go in a k6 v2 options object?Under the root `cloud` key. The `options.ext.loadimpact` block that older k6 material shows was removed in k6 v2.0.0. `ext` still parses as a root key, so an `ext.loadimpact` block does not fail the run - it is simply ignored, which makes it a quiet way to lose configuration when porting an old script.
saying these in an interview costs you the question
- Thinks k6 needs a separate YAML or XML configuration file
- Says k6 re-reads the options object once per iteration
- Assumes every options key has a matching k6 run flag
- Puts cloud settings under ext.loadimpact, removed in k6 v2.0.0
- Calls the CLI flag --stages; k6's flag is singular --stage