release notes/v2.0.0.md
k6 v2.0.0 is here 🎉!
k6 v2.0.0 is the final release of the v2 major version, completing the cleanup of deprecated APIs, old commands, and obsolete configuration options that was started with v2.0.0-rc1. If you were already running the release candidate, this release includes a handful of additional changes on top — they are marked with (new since v2.0.0-rc1) throughout these notes.
Here's a glimpse of what's changed in this release:
go.k6.io/k6/v2 — all extensions must update their import paths to be compatible with v2.k6 login, k6 pause, k6 resume, k6 scale, k6 status, --no-summary, --upload-only, and more.externally-controlled executor has been removed — scripts using executor: externally-controlled will no longer run.97 instead of 0.options.ext.loadimpact is no longer supported — use options.cloud.k6/experimental/redis module has been removed.k6 cloud script.js positional form has been fully removed — use k6 cloud run script.js.k6 cloud commands — the previous fallback to the first available stack has been removed.encoding/json — extension authors relying on easyjson-generated methods on k6 types must update.--address to enable it.k6 cloud project list command to list Grafana Cloud k6 projects.k6 cloud run --local-execution; use --no-cloud-secrets to opt out.These are changes that require you to update your scripts, CI/CD pipelines, or configuration files before upgrading.
go.k6.io/k6/v2 #5777Following the Go module versioning conventions, the k6 module path has changed from go.k6.io/k6 to go.k6.io/k6/v2.
Any extension or external package that imports go.k6.io/k6 must update all import paths to go.k6.io/k6/v2. For the vast majority of extensions this is the only change needed — a mechanical find-and-replace across the codebase:
go.k6.io/k6/ → go.k6.io/k6/v2/
For example:
// Before
import "go.k6.io/k6/js/modules"
// After
import "go.k6.io/k6/v2/js/modules"
The following commands for controlling a running test have been removed. They have not been functional for most use cases since the REST API they relied on was limited to specific execution modes:
k6 pausek6 resumek6 scalek6 statusMigration: There is no replacement. These commands relied on the externally-controlled executor, which has also been removed in v2.0.0 (see below).
externally-controlled executor #5846The externally-controlled executor has been removed. It was legacy code from an older k6 Cloud architecture that allowed external systems to scale VUs and pause/resume a running test via the k6 REST API — the capability that k6 pause, k6 resume, k6 scale, and k6 status relied on.
Migration: There is no replacement. Any test script with executor: externally-controlled will fail to start. Migrate to a different executor based on the desired load profile (e.g., ramping-vus, constant-vus, constant-arrival-rate).
k6 login command #5134The top-level k6 login command and its subcommands (k6 login cloud, k6 login influxdb) have been removed.
Migration:
k6 login cloud → k6 cloud loginK6_INFLUXDB_* to configure the InfluxDB output directly.k6 cloud script.js positional form #5624, #5912 (completed in v2.0.0)The old positional-argument form k6 cloud script.js has been fully removed. In v2.0.0-rc1 it was changed to show help instead of running; in v2.0.0 the deprecated command handler itself has been removed entirely. The run subcommand has been the recommended path since k6 cloud run was introduced.
Migration: Replace k6 cloud script.js with k6 cloud run script.js.
--upload-only flag #5844The --upload-only flag on the k6 cloud command has been removed.
Migration: Use k6 cloud upload script.js to upload a test without running it.
--no-summary flag #5729The --no-summary flag has been removed.
Migration: Replace --no-summary with --summary-mode=disabled.
--summary-mode=legacy #5730The legacy value for --summary-mode has been removed.
Migration: There is no direct equivalent — the new summary format is different from the legacy one. Review the available summary modes and choose the one that best fits your needs: compact (the default) or full for more detailed output.
options.ext.loadimpact support #5774The options.ext.loadimpact configuration block in test scripts is no longer supported.
Migration: Move all cloud-related configuration from options.ext.loadimpact to options.cloud:
// Before
export const options = {
ext: {
loadimpact: {
projectID: 12345,
name: "My Test",
},
},
};
// After
export const options = {
cloud: {
projectID: 12345,
name: "My Test",
},
};
k6/experimental/redis module #5485The k6/experimental/redis module has been removed from the k6 core binary. It was shipped as an experiment and has not been promoted to stable.
Migration: Change your import from k6/experimental/redis to k6/x/redis. With auto-extension-resolution, k6 will automatically provision the xk6-redis extension when it sees the k6/x/redis import.
ExporterType option from OpenTelemetry output #5754The deprecated exporterType configuration option for the OpenTelemetry output has been removed.
Migration: Replace K6_OTEL_EXPORTER_TYPE with K6_OTEL_EXPORTER_PROTOCOL. The accepted values are grpc and http/protobuf.
SingleCounterForRate option from OpenTelemetry output #5830The temporary SingleCounterForRate escape-hatch option for the OpenTelemetry output has been removed. It was introduced as a one-release migration aid in #5164 to let users revert to the old pair-of-counters format (<metric>.occurred + <metric>.total) while upgrading. Rate metrics are now always exported as a single counter with a condition attribute (nonzero/zero).
Migration: Remove any K6_OTEL_SINGLE_COUNTER_FOR_RATE=true configuration. If you were using the old pair-of-counters format, update your dashboards and queries to use the single counter with condition attribute instead.
K6_BINARY_PROVISIONING environment variable #5734The K6_BINARY_PROVISIONING environment variable, deprecated in v1.2.0, has been removed.
Migration: Remove K6_BINARY_PROVISIONING from your environment. Auto-extension-resolution is enabled by default; K6_AUTO_EXTENSION_RESOLUTION only needs to be set explicitly if you want to disable it.
K6_ENABLE_COMMUNITY_EXTENSIONS environment variable #5733The K6_ENABLE_COMMUNITY_EXTENSIONS environment variable has been removed. The community and cloud extension catalogs were merged server-side, making this flag a no-op since the catalogs were unified.
Migration: Remove K6_ENABLE_COMMUNITY_EXTENSIONS from your environment. Community extensions are now resolved through the default build service URL automatically.
k6 cloud commands #5833Providing a stack is now mandatory for all k6 cloud commands (k6 cloud run, k6 cloud upload, k6 cloud run --local-execution). Previously, omitting a stack would fall back to the first available stack with a deprecation warning — that fallback has been removed. Likewise, k6 cloud login now requires both a token and a stack; passing one without the other fails with an explicit error.
Migration: Run k6 cloud login which will ask you for the stack and setup correctly for the new version of k6. Alternatively, the K6_CLOUD_STACK_ID environment variable, or the stackID script option are available to be set before running any k6 cloud command .
k6 no longer automatically migrates configuration files from the old {USER_CONFIG_DIR}/loadimpact/config.json path introduced before k6 v1.0.0.
Migration: If you still have a config file at the old path, move it to {USER_CONFIG_DIR}/k6/config.json. You can also re-run k6 cloud login to regenerate the file at the correct location.
Previously, most cloud abort scenarios returned an exit code of 0, making it impossible for CI pipelines to distinguish a failed run from a successful one. These scenarios now return exit code 97.
| Scenario | Before | After |
|---|---|---|
| Finished | 0 | 0 |
| Aborted by threshold | 99 | 99 |
| Aborted by system, limit, script error, user, or timeout | 0 | 97 |
Migration: Review any CI pipeline logic that treats exit code 0 as success for cloud runs. Cloud runs that abort for reasons other than threshold violations will now return exit code 97.
The browser_web_vital_fid metric has been removed. FID (First Input Delay) was deprecated as a Core Web Vital and replaced by INP (Interaction to Next Paint). The embedded web-vitals library has been updated from v3 to v5.1.0, which reflects this change.
Migration: Update any dashboards, alerts, or threshold rules that reference browser_web_vital_fid to use browser_web_vital_inp instead.
k6 no longer uses easyjson for JSON serialization. Several types in the public Go API previously had easyjson-generated MarshalJSON/UnmarshalJSON methods; those are now gone, replaced by stdlib encoding/json. This change was intentionally held until v2 to avoid a breaking change mid-cycle.
Migration: Extension authors who relied on easyjson-generated methods on k6 types should remove those dependencies. Standard encoding/json marshaling applies to all affected types.
The k6 HTTP API server no longer starts automatically. Previously, k6 always listened on localhost:6565 unless opted out. The server now only starts when an address is explicitly provided via the --address flag or the K6_ADDRESS environment variable.
Migration: If you use the k6 REST API, pass --address=localhost:6565 (or any address) to re-enable the server.
A new built-in secret source, cloud, allows k6 scripts to retrieve secrets stored in Grafana Cloud directly during local execution. Pass --secret-source=cloud when running k6 cloud run --local-execution to make cloud-managed secrets available to your script via secrets.get(). Thanks, @vortegatorres!
k6 cloud run --local-execution --secret-source=cloud script.js
--secret-source=cloud is no longer required. When running k6 cloud run --local-execution, the cloud secret source is now automatically available and secrets.get() works out of the box.
To opt out, pass --no-cloud-secrets:
k6 cloud run --local-execution --no-cloud-secrets script.js
k6 cloud project list command (new since v2.0.0-rc1) #5650A new k6 cloud project command group has been added, with a k6 cloud project list subcommand for listing Grafana Cloud k6 projects. The output supports both human-readable table format and JSON (--format=json).
k6 cloud project list
k6 cloud project list --format=json
When using shell tab-completion, pressing TAB after a fully-typed extension name (e.g. k6 x docs <TAB>) will now automatically provision a custom binary with that extension and delegate the completion request to it. This means extension-specific flags and subcommands are available via tab-completion without any manual setup.
When creating a k6 archive (k6 archive), extension dependency information is now captured in metadata.json under a "dependencies" field, using the constraints declared by the script before any external manifest overrides are applied. This ensures that k6/x/ imports are preserved correctly during auto-extension-resolution re-execution.
http.get() or http.head() receive extra arguments that are silently ignored, helping catch common scripting mistakes. Thanks, @moko-poi!newAction-based Locator APIs in the browser module, improving reliability of browser tests. Thanks, @janHildebrandt98!k6 x docs hint to the main k6 help output for built-in documentation discovery. Thanks, @Reranko05!k6 x explore hint to the main k6 help output for extension discovery.K6_PROVISION_HOST_VERSION, so extension subcommands can correctly identify the caller's k6 version when showing docs or feature information.v0.0.0+sha or v0.0.0-timestamp-sha), even when the running binary already satisfied the constraint.k6 cloud run TUI issues: ghost duplicate progress bars appearing after the run finishes, a timer that stalled and then jumped to 100%, and a stale timer racing with the status line.k6 cloud run --local-execution ignoring K6_CLOUD_PUSH_REF_ID and unconditionally creating a new test run instead of reusing the provided run ID. Thanks, @Reranko05!bufferedAmount not being incremented when sending TypedArrays, causing it to go negative. Thanks, @prakharbirla-ng!handleExitEvent where Done() was signalled before the subscription was removed, causing tests to hang until the timeout.ElementHandle.DefaultTimeout (and any method that calls it, such as GetAttribute) when invoked on a nil or partially-initialized handle. Thanks, @SAY-5!TaskQueue from the external github.com/mstoykov/k6-taskqueue-lib into internal/js/taskqueue, eliminating an awkward reverse dependency where the library's own tests depended on k6.internal/dashboard, making the web dashboard a built-in part of the k6 binary without requiring a separate extension. The dashboard is available via k6 run --out=web-dashboard.synctest package within lib/executor, eventloop, timer, PeriodicFlusher, output/cloud, and output/cloud/expv2 tests for virtualized time, improving test determinism and CI speed.go.opentelemetry.io/otel/exporters/otlp/otlptracehttp and otlpmetrichttp to v1.43.0 [security], adding a 4 MiB response body cap to mitigate memory exhaustion from misconfigured or malicious servers.go.opentelemetry.io/otel packages.github.com/grafana/sobek digest.github.com/grafana/k6-cloud-openapi-client-go.github.com/evanw/esbuild to v0.28.0.buf.build/gen/go/prometheus protobuf.github.com/andybalholm/brotli to v1.2.1.examples/ dependencies.github.com/fatih/color to v1.19.0.github.com/puerkitobio/goquery to v1.12.0.github.com/mattn/go-isatty to v0.0.22.v1.25.10.github.com/grafana/k6provider to v0.5.0.crazy-max/ghaction-chocolatey to v4.actions/setup-go to 4a36011.anchore/sbom-action to v0.24.0.github/codeql-action to v4.35.2.docker/login-action to v4.1.0.grafana/shared-workflows actions.actions/upload-artifact.v1.26.3).Connection: close response.github.com/klauspost/compress to v1.18.5.golang.org/x packages.v3.23.4.K6ModPath to the v2 module path in the k6provider config so that k6 v2 correctly signals to k6build which module to resolve against (go.k6.io/k6/v2).:latest or the GitHub "Latest release" marker. Adds a floating :vN Docker tag (e.g. grafana/k6:v1) for users who want to pin to a major line.golang.org/x/net to v0.53.0 [security] (CVE-2026-33814).github.com/Masterminds/semver/v3 to v3.5.0.github.com/Azure/go-ntlmssp to v0.1.1.A huge thank you to the external contributors who helped during this release: @moko-poi, @janHildebrandt98, @Reranko05, @prakharbirla-ng, @esquonk, @chrismooreproductions, @vortegatorres, @SAY-5, and @LBaronceli! 🙏