Which of k6's four metric types does each built-in such as vus, http_reqs or checks use?
answer
- four types, one fixed per metric
- counts versus durations versus levels
- only two Gauges, only two Rates
- vus and vus_max are the Gauges
- every _duration built-in is a Trend
basics
~20 sk6 has four metric types: Counter, Gauge, Rate and Trend. Among the built-ins, vus and vus_max are Gauges, http_reqs and iterations are Counters, checks and http_req_failed are Rates, and every duration such as http_req_duration is a Trend.
solid answer
~30 sk6 registers every metric as one of **Counter**, **Gauge**, **Rate** or **Trend**, and each built-in's type is fixed by k6 itself. The two Gauges are `vus` and `vus_max`. The Counters are the tallies: `iterations`, `http_reqs`, `dropped_iterations`, `data_sent`, `data_received` and the WebSocket message counts. The two Rates are `checks` and `http_req_failed`. Everything that measures a duration is a Trend — `http_req_duration` and the rest of the `http_req_*` timings, plus `iteration_duration`, `group_duration`, `grpc_req_duration` and the WebSocket timings. The rule of thumb in k6 v2: counts are Counters, durations are Trends, and only four built-ins fall outside that.
go deeper
Learn the four type names — Counter, Gauge, Rate, Trend — and be able to place vus, http_reqs, checks and http_req_duration correctly. That mapping alone answers most screening questions.
Explain why each built-in got its type: tallies are summed, levels are sampled, pass proportions are rates, and durations need a distribution rather than a single figure.
Know the whole roster including the WebSocket and gRPC built-ins, and be able to say instantly which candidate names — http_req_total, checks_failed, vus_active — are not real k6 metrics.
Recognise that the fixed type of each built-in is a contract k6 sets for you, so any shared convention your teams build on these names inherits both the name and the type.
## k6's four metric types Every metric in k6 — built-in or otherwise — is one of exactly four types, and the type is fixed when the metric is registered: - **Counter** — sums the values added to it. - **Gauge** — tracks the smallest, the largest and the latest value. - **Rate** — tracks how often a non-zero value occurs. - **Trend** — accumulates many values and computes statistics over them. You choose the type for a metric of your own. For the built-ins you do not: k6 registers each of them at startup with a type already decided, and that decision is what tells you how the numbers behave. ## The built-in roster by type In k6 v2 the built-in metrics and their registered types are: | Built-in metric | Type | What it measures | |---|---|---| | `vus` | Gauge | Virtual users currently active | | `vus_max` | Gauge | Virtual users k6 has initialised | | `iterations` | Counter | Completed iterations of the default function | | `iteration_duration` | Trend | Wall-clock time of one full iteration | | `dropped_iterations` | Counter | Iterations that were not started | | `checks` | Rate | Rate of passing checks | | `group_duration` | Trend | Time spent inside a group | | `http_reqs` | Counter | HTTP requests k6 generated | | `http_req_failed` | Rate | Rate of requests judged failed | | `http_req_duration` and the other `http_req_*` timings | Trend | Per-phase request times | | `data_sent`, `data_received` | Counter | Bytes written and read on the wire | | `ws_sessions`, `ws_msgs_sent`, `ws_msgs_received` | Counter | WebSocket session and message tallies | | `ws_ping`, `ws_session_duration`, `ws_connecting` | Trend | WebSocket timing measurements | | `grpc_req_duration` | Trend | gRPC response time | Three patterns run through that table and are worth holding onto: 1. **Anything that counts occurrences is a Counter** — `iterations`, `http_reqs`, `dropped_iterations`, `data_sent`, `data_received`, and the WebSocket message tallies. 2. **Anything that is a duration is a Trend** — every `http_req_*` timing, plus `iteration_duration`, `group_duration`, `ws_session_duration`, `ws_ping`, `ws_connecting` and `grpc_req_duration`. 3. **Only two built-ins are Rates** (`checks` and `http_req_failed`) and **only two are Gauges** (`vus` and `vus_max`). ## Value units are a second, separate property Beyond the type, k6 registers some built-ins with a value unit. The duration Trends carry a **time** unit and `data_sent`/`data_received` carry a **data** unit, which is why k6 renders one family in milliseconds and the other in bytes. The unit does not change the type: a byte Counter is still a Counter. ## Names that look like built-in metrics but are not Two traps sit next to this roster: - **`checks_total`, `checks_succeeded` and `checks_failed`** appear in k6's end-of-test summary as three display lines derived from the single `checks` Rate. They are presentation names, not metrics k6 registers. - **`http_req_total`, `http_req_looking_up` and `vus_active`** do not exist in k6 at all. The real names are `http_reqs` for the request count and `vus` for the active-VU count, and k6 has no built-in for the DNS lookup phase. Two further name confusions are worth naming explicitly: - **The request counter is `http_reqs`** — plural, with no `_req_` timing prefix — while every timing metric is singular, `http_req_*`. - **There is one built-in named `checks`.** k6 does not mint a separate metric per check name, so no `checks_login` or `checks_status_200` metric exists. ## Where a built-in's type is fixed k6 keeps a single registry of metrics keyed by name, and each entry carries its type. The built-ins are registered into that registry when k6 starts, before your script runs, which is why you never choose their types and why the same name always means the same type across every run. Two rules fall out of the registry's design: 1. **A metric name maps to exactly one type.** Registering a name that already exists with a different type is an error, not a redefinition. 2. **Names obey a fixed shape.** A k6 metric name must start with a letter or an underscore and contain only ASCII letters, digits and underscores, up to 128 characters — which is why every built-in reads as lowercase words joined by underscores. That shape is also a useful sanity check on a half-remembered name: anything with a dot, a dash or a capital letter in it is not a k6 metric name. ## Why the type is the fact to remember The type is what makes a built-in's numbers legible. `vus` being a Gauge is why it reports a current level rather than a running total; `http_reqs` being a Counter is why it only ever grows; `checks` being a Rate is why it is expressed as a proportion; and `http_req_duration` being a Trend is why k6 has a distribution of values for it rather than one number. Learn the roster above and the behaviour of every built-in follows from it.
- Which two built-in k6 metrics are Rates, and what does each one's rate express?`checks` and `http_req_failed`. `checks` expresses the proportion of check evaluations that passed, and `http_req_failed` the proportion of HTTP requests k6 judged to have failed. A Rate in k6 tracks how frequently a non-zero value occurs, so both read as a fraction.
- Is checks_failed a built-in k6 metric you can find in the metric stream?No. k6 registers a single built-in Rate named `checks`. `checks_total`, `checks_succeeded` and `checks_failed` are three display lines the end-of-test summary derives from it, not metrics of their own.
- Why is data_received a Counter rather than a Trend even though it reports kilobytes?Because it tallies bytes, and a tally is summed rather than distributed. k6 registers it as a Counter with a data value unit; the unit governs how the figure is rendered, while the Counter type governs that the values are added up.
saying these in an interview costs you the question
- Calling http_req_duration a Gauge because it reports a current time
- Naming the request counter http_req_total instead of http_reqs
- Treating checks_failed as a registered k6 metric rather than a summary line
- Saying vus is a Counter because it goes up during a ramp
- Believing you pick the type of a built-in metric yourself