skip to content

In a k6 scenarios map, what is `startTime` measured from, and how do you make two scenarios run back to back?

level: seniorimportance: must knowfreq 64%

answer

  1. no dependsOn key exists
  2. measured from the same origin
  3. everything starts at once, then waits
  4. add the graceful window to the offset

basics

~20 s

startTime is an absolute offset from the start of the k6 run, not from the previous scenario's end. k6 launches every scenario at once and each waits out its own offset, so sequencing is arithmetic you do yourself.

solid answer

~40 s

k6 has no "run after" key. When the run begins it starts every scenario in the map concurrently, and each one sleeps until its own `startTime` offset — measured from the start of the run, after VU initialisation and `setup()` — before its executor does anything. To run `warm_up` and then `peak` back to back you set `peak`'s `startTime` to the wall-clock length of `warm_up`, and that length is the executor's own duration **plus** its `gracefulStop`, since the earlier scenario can still be finishing iterations inside that window. If the windows do overlap the scenarios simply run in parallel — which is legitimate, but k6 then has to pre-initialise the sum of the VUs both need at that instant, which is what the *max VUs* figure in the run header reports.

code

javascript · 24 lines
javascript
import http from 'k6/http';

export const options = {
  scenarios: {
    warm_up: {
      executor: 'constant-vus',
      exec: 'browse',
      vus: 5,
      duration: '30s',
      gracefulStop: '5s',
    },
    peak: {
      executor: 'constant-vus',
      exec: 'browse',
      vus: 50,
      duration: '2m',
      startTime: '35s',
    },
  },
};

export function browse() {
  http.get('https://quickpizza.grafana.com/');
}

go deeper

for a junior

Remember that startTime offsets a scenario from the beginning of the run and defaults to 0s, so scenarios without it all start together. There is no key that chains one scenario to another.

for a middle

Explain that k6 starts every scenario concurrently and each waits out its own offset, and compute a sequential offset as the earlier scenario's length plus its gracefulStop.

for a senior

Show how overlap propagates into the merged execution plan and the initialised VU pool, and read the max VUs and max duration figures in the run header back to the map that produced them.

for a principal

Decide when a staggered map is the right shape at all: it keeps one run, one summary and one exit code, but it also fixes the whole schedule at config time and pays VU initialisation for the peak.

## `startTime` is an absolute offset `startTime` is one of the keys every k6 scenario entry accepts. It is a duration string, it defaults to `"0s"`, and a negative value is rejected at config time with *the startTime can't be negative*. The crucial property is what it is relative to: **the start of the test run**, not the end of any other scenario. There is no dependency between entries in the map — no `after`, no `dependsOn`, no ordering by position in the object. Mechanically, k6 launches one goroutine per scenario as soon as the run starts. A scenario whose `startTime` is greater than zero sits in a waiting state — its progress bar reads `waiting` with a countdown — and only then hands control to its executor. This is why scenarios are described as running in parallel and only *appear* sequential when you arrange the offsets so their windows do not overlap. ## Staggering two scenarios The worked shape is two named scenarios, the second offset past the first: ```javascript export const options = { scenarios: { warm_up: { executor: 'constant-vus', exec: 'browse', vus: 5, duration: '30s', gracefulStop: '5s', }, peak: { executor: 'constant-vus', exec: 'browse', vus: 50, duration: '2m', startTime: '35s', }, }, }; ``` `warm_up` starts at zero and stops handing out iterations at 30 seconds, but its graceful window keeps it alive until 35 seconds. `peak` is offset to exactly that point, so the two never overlap. ## Doing the arithmetic The offset you need for a genuinely sequential run is not the previous scenario's `duration`: 1. take the earlier scenario's own length — its `duration`, the sum of its stages, or its iteration ceiling; 2. add that scenario's `gracefulStop`, which defaults to `30s` and extends its slot in k6's execution plan; 3. add the earlier scenario's own `startTime`, because every offset is measured from the same origin; 4. use the total as the later scenario's `startTime`. Step 2 is the one people miss. A `duration: '30s'` scenario left on the default graceful stop occupies the plan until second 60, so a second scenario at `startTime: '30s'` overlaps it for half a minute. ## Executors you cannot time exactly Sequencing is only reliable when the earlier scenario has a length k6 knows in advance. Executors configured by a duration or by stages do; iteration-based ones finish whenever their iterations are done and carry only a ceiling, so a scenario built on them may end long before the offset you computed. In practice that is fine — you get a gap rather than an overlap — but it means "back to back" is a floor, not a guarantee. ## What overlap actually costs Overlapping scenarios are a normal thing to want, and k6 supports them directly. The cost is in VU allocation. Before the run k6 merges every scenario's VU requirements into a single execution plan, adding the requirements of any scenarios whose windows coincide, and initialises one pool sized to the peak of that merged curve. All scenarios then draw from that one pool. - Two 10-VU scenarios that overlap need 20 initialised VUs; the run header prints `2 scenarios, 20 max VUs`. - The same two scenarios offset so they never overlap need only 10. - The header's *max duration (incl. graceful stop)* is the end of the last scenario's window, so it includes both the largest `startTime` and that scenario's graceful stop. - k6 lists the scenarios in the header sorted by `(startTime, name)`, so the printed order is the schedule, not the order you wrote them. Because every VU is initialised before the run starts, VU initialisation cost is paid for the peak even if the peak lasts one second. Staggering scenarios so their peaks do not coincide is therefore also a way of keeping the initialised VU count down. | two 10-VU scenarios, each `duration: '1m'` | offsets | initialised VUs | wall clock | |---|---|---|---| | both left at the default offset | `0s` and `0s` | 20 | about 1m30s | | second offset past the first's duration only | `0s` and `60s` | 20 | about 2m30s | | second offset past duration plus graceful stop | `0s` and `90s` | 10 | about 3m | The middle row is the trap: it looks sequential, costs the VUs of a parallel run, and nothing in the script says so — only the header's max VUs figure does. ## Common mistakes - Treating `startTime` as a delay after the previous scenario, then wondering why three 1-minute scenarios all finish within a minute of each other. - Forgetting `gracefulStop` in the arithmetic and getting a silent overlap. - Assuming the map's property order controls execution order; it does not, and object key order is not part of the contract. - Expecting `startTime` to delay `setup()` — it does not. `setup()` runs once, before any scenario's clock starts, and the offsets are measured from after it.

  • How many VUs does k6 initialise for two 10-VU scenarios that overlap for part of the run?
    Twenty. k6 merges the scenarios' VU requirements into one execution plan, sums the requirement of any scenarios whose windows coincide, and initialises a single pool sized to the peak of that curve. The run header reports it as `2 scenarios, 20 max VUs`. Offset them so they never overlap and the same script needs only ten.
  • Does a scenario's `startTime` delay `setup()` in a k6 script?
    No. `setup()` runs once for the whole test, before any scenario begins, and the `startTime` clock starts after it. So a scenario with `startTime: '5m'` does not postpone setup work by five minutes — it postpones only its own executor, and the setup data is already there when it starts.
  • Does the order of keys in the k6 `scenarios` object affect execution order?
    No. Execution order comes entirely from `startTime`; k6 sorts scenarios by `(startTime, name)` for display and scheduling bookkeeping, using the scenario name only to break ties between equal offsets. Two entries with no `startTime` both begin at zero regardless of which was written first.

Timetabling by clock time rather than by queue: every scenario is told the minute it starts, so if you want one to follow another you have to work out that minute yourself.

saying these in an interview costs you the question

  • Reading startTime as a delay after the previous scenario ends
  • Assuming scenarios run in the order they appear in the object
  • Omitting gracefulStop when computing the next scenario's offset
  • Expecting k6 to have a dependsOn or after key
  • Thinking overlapping scenarios each get their own separate VU pool