> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/resilience-testing/load-testing/create-a-load-test/k6.md).

# JavaScript

The **JavaScript** Load Test Engine is based on [k6](https://k6.io/). Use it when you want a test that does more than generate traffic: it scripts realistic user flows, enforces pass/fail criteria, and runs the same way locally and in continuous integration. In Harness Resilience Testing, you build a JavaScript test in the **Load Test Studio** and run it on your Kubernetes infrastructure.

{% hint style="info" %}
**JAVASCRIPT RUNS ON KUBERNETES ONLY**

JavaScript load tests run on **Kubernetes** infrastructure. To run on a Linux VM, use [Python](/resilience-testing/load-testing/create-a-load-test.md).
{% endhint %}

***

### What you can do with JavaScript <a href="#what-you-can-do-with-javascript" id="what-you-can-do-with-javascript"></a>

Each goal below maps to a k6 capability you configure on this page:

* **Gate a release on performance.** Declare a [threshold](#gate-a-release-with-passfail-thresholds) such as "95th-percentile latency under 5000 ms" so a regression fails the run automatically.
* **Reproduce a traffic surge.** Write a [stage sequence](#shape-the-load) that ramps from zero to a high peak and back, mimicking a sale or launch.
* **Find the breaking point.** Push past expected load in stages until the system degrades.
* **Catch slow leaks.** Hold steady load for hours to surface memory leaks or resource exhaustion.
* **Model real journeys.** Run [several scenarios](#shape-the-load) in parallel, such as a browse flow and a checkout flow, each with its own schedule.
* **Drive very high load.** Use [distributed execution](#scale-with-distributed-execution) to split a test across runner pods for high concurrency.

***

### Prerequisites <a href="#prerequisites" id="prerequisites"></a>

* **Module access:** Access to the Harness Resilience Testing module.
* **Kubernetes infrastructure:** A [Kubernetes Chaos Infrastructure](/resilience-testing/chaos-testing/infrastructure/kubernetes.md) (v1.85.3 or later) with load testing enabled. Load testing is enabled by default on Kubernetes infrastructure.
* **Environment:** An environment created in your project for the infrastructure.
* **An onboarded service:** At least one service onboarded against that infrastructure.
* **Reachable target:** Target application endpoints accessible from the test infrastructure.
* **A k6 script:** A `.js` file, or a container image that carries one. Harness runs the script you supply and does not generate one for you.

***

### Create a load test <a href="#create-a-load-test" id="create-a-load-test"></a>

1. Navigate to **Resilience Testing** > **Load Testing**.
2. Click **+ New Load Test**.

{% hint style="info" %}
**TRY A SAMPLE TEST**

Click the arrow beside **+ New Load Test** and select **Try K6 Sample Test** to explore the flow with a pre-configured test before you build your own.
{% endhint %}

#### Configure the load test overview <a href="#configure-the-load-test-overview" id="configure-the-load-test-overview"></a>

On the **Overview** tab, enter the test metadata and select where the test runs:

| Field                           | Description                                                                                                                 |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **Name**                        | A descriptive identifier for the test. Use lowercase letters, numbers, and dashes only. Harness derives the **Id** from it. |
| **Description**                 | (Optional) What the test validates.                                                                                         |
| **Tags**                        | (Optional) Labels to organize tests.                                                                                        |
| **Target Type**                 | Select **Kubernetes**. The agent in the cluster runs k6 as a master pod and optional runner pods.                           |
| **Load Test Infrastructure**    | Select a Kubernetes Chaos Infrastructure from the dropdown.                                                                 |
| **Resilience Testing Services** | Select at least one onboarded service the test targets. This section appears once the infrastructure is set.                |
| **Load Test Engine**            | Select **JavaScript** (Based on K6).                                                                                        |

#### Select the services under test <a href="#select-the-services-under-test" id="select-the-services-under-test"></a>

The **Services** section appears after you choose an infrastructure, and it is required. The picker lists only the services onboarded against that infrastructure, so a test cannot reference a workload the infrastructure does not manage.

If the infrastructure has no onboarded services, the section reads **No resilience testing services yet** and offers **Onboard a Service**. You cannot continue until at least one service exists, because Harness reports load results against the service rather than against the test alone.

Click **Next** to proceed to **Test Configuration**.

***

### Define the test <a href="#define-the-test" id="define-the-test"></a>

On the **Test Configuration** tab, choose how you want to supply the k6 script. You write the workload in JavaScript, and Harness runs the script exactly as you provide it.

{% tabs %}
{% tab title="Upload K6 script" %}
Upload a custom k6 JavaScript file when you need full control over user behavior, custom logic, or advanced k6 features. The script must export a default function: `export default function () { ... }`.

| Field              | Description                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| **Host URL**       | Passed to your script as `__ENV.HOST_URL`. Leave blank if your script uses hardcoded URLs.                            |
| **k6 Script File** | Drag and drop or browse to upload a `.js` file. Available globals are `__ENV.HOST_URL` and any variables you declare. |

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 50 },
    { duration: '5m', target: 50 },
    { duration: '1m', target: 0 },
  ],
  thresholds: {
    http_req_duration: ['p(95)<5000'],
  },
};

export default function () {
  const res = http.get(`${__ENV.HOST_URL}/api/products`);
  check(res, { 'status is 200': (r) => r.status === 200 });
  sleep(1);
}
```

When your script declares its own scenarios, stages, or thresholds, k6 runs them as written. The **Load Profile Overrides** below apply only when the script does not define its own scenarios.
{% endtab %}

{% tab title="Using Custom Image" %}
Use a prebuilt container image as the load test source. The image must contain the k6 binary and your script. This is useful when you maintain a packaged k6 setup outside Harness.

| Field                   | Description                                                                                                                   |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Host URL**            | Passed to your script as `__ENV.HOST_URL`. Leave blank if your script uses hardcoded URLs.                                    |
| **Load Test Image**     | Container image reference (for example, `my-registry/my-load-test:latest`).                                                   |
| **Entrypoint**          | Path to the `.js` script inside the image, passed as the positional argument to `k6 run` (for example, `/scripts/script.js`). |
| **Load args**           | Additional CLI flags appended to the `k6 run` command (for example, `--quiet`, `--out=json=/tmp/r.json`).                     |
| **Image Registry Type** | Select **Public** for an image anyone can pull, or **Private** to pull with credentials.                                      |
| **Secret Name**         | The Kubernetes image pull secret used to authenticate. This field appears only when **Image Registry Type** is **Private**.   |

The **Load Profile Overrides** always take precedence over the image. If your script defines its own scenarios or stages, set **Duration** to their total run time so the complete test runs.
{% endtab %}
{% endtabs %}

***

### Shape the load <a href="#shape-the-load" id="shape-the-load"></a>

How virtual users (VUs) are scheduled over time comes from your script. Declare `scenarios` or `stages` in `options`, and k6 runs them as written.

```javascript
export const options = {
  scenarios: {
    browse: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 500 },
        { duration: '1m', target: 0 },
      ],
    },
  },
};
```

The `ramping-vus` executor ramps VUs linearly to each target and is the most common choice for gradual load. Add a second entry under `scenarios` to run workloads in parallel, such as a browse flow alongside a checkout flow. Go to the [k6 executors reference](https://grafana.com/docs/k6/latest/using-k6/scenarios/executors/) to review the other executors.

#### Override the profile <a href="#override-the-profile" id="override-the-profile"></a>

**Load Profile Overrides** is a small set of fields on the **Test Configuration** tab for tests whose script does not declare its own schedule. The overrides apply only to k6's implicit default scenario, and k6 ignores them once your script declares scenarios or stages.

| Override               | Description                                                                                  |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| **Users**              | Concurrent virtual users to run.                                                             |
| **Duration (seconds)** | Total run time. For scripted scenarios or stages, enter their combined duration.             |
| **Iterations (cap)**   | Maximum iterations. Leave blank to use the value from the script.                            |
| **RPS Limit**          | A global cap on requests per second that always applies. It is split evenly across replicas. |

Once you upload a script and set **Users** and **Duration**, the panel beside these fields previews the resulting steady-state profile.

***

### Gate a release with pass/fail thresholds <a href="#gate-a-release-with-passfail-thresholds" id="gate-a-release-with-passfail-thresholds"></a>

Thresholds turn a load test into a gate. If any threshold fails, the run is marked **Failed**, which makes k6 well suited to release gates in continuous integration.

k6 thresholds are declared in the script under `options.thresholds`, not in the Load Test Studio. Each entry names a metric and the condition it must satisfy.

```javascript
export const options = {
  thresholds: {
    http_req_duration: ['p(95)<5000'],
    http_req_failed: ['rate<0.01'],
  },
};
```

The example fails the run if the 95th-percentile request duration reaches 5000 ms, or if more than one percent of requests fail. Harness reports the outcome of each threshold in a **Thresholds** table on the run detail page. A run that declares no thresholds shows no such table.

To abort as soon as a threshold is breached rather than finishing the test, add `abortOnFail` using the [k6 threshold options](https://grafana.com/docs/k6/latest/using-k6/thresholds/).

***

### Add environment variables <a href="#add-environment-variables" id="add-environment-variables"></a>

Use the **Environment Variables** section to pass configuration and secrets into your test without hardcoding them. Reference them inside the script as `__ENV.NAME`. Secrets are encrypted at rest.

***

### Scale with distributed execution <a href="#scale-with-distributed-execution" id="scale-with-distributed-execution"></a>

When a single pod cannot generate enough load, distributed execution runs k6 across multiple runner pods on Kubernetes. Each replica runs a slice of the load through k6's `--execution-segment` flag, so the total VUs and RPS are split evenly across pods.

| Replicas      | Behavior                                                                                               |
| ------------- | ------------------------------------------------------------------------------------------------------ |
| **0 or 1**    | Single-pod mode. All VUs run on one pod.                                                               |
| **2 or more** | The load is split across pods. Use this for more than 1,000 VUs or sustained high requests per second. |

***

### Set a value at run time <a href="#set-a-value-at-run-time" id="set-a-value-at-run-time"></a>

Every tool input carries a pin control at the end of the field. Select it to switch the field between **Fixed value** and **Runtime input**.

A fixed value is stored with the test and used on every run. A runtime input leaves the field unset, so the value is supplied when the test runs, which lets one load test serve several environments or load levels. Go to [Run a load test in a pipeline](/resilience-testing/load-testing/run-in-a-pipeline.md) to supply these values from a pipeline.

***

### Define variables <a href="#define-variables" id="define-variables"></a>

**Variables** is a drawer on the right edge of the Load Test Studio. A variable holds a value once and supplies it to the tool inputs, so a value such as a host name or a thread count lives in one place instead of being repeated across fields.

Select **+ Add Variable** and complete the **New Variable** dialog:

| Field           | Description                                                                                                              |
| --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **Type**        | **String**, **Number**, or **Secret**. Use **Secret** for credentials so the value is not stored in the test definition. |
| **Name**        | The variable name.                                                                                                       |
| **Value**       | The value to use. This field carries its own pin control, so a variable can itself be a runtime input.                   |
| **Description** | (Optional) What the variable is for.                                                                                     |

Select **Save** to add the row, then **Apply Changes** to keep the drawer's edits. The drawer lists each variable with its **Variable**, **Description**, and **Value**.

***

### Tune pods with Advanced Options <a href="#tune-pods-with-advanced-options" id="tune-pods-with-advanced-options"></a>

**Advanced Options** is a drawer on the right edge of the Load Test Studio that controls how the load pods themselves behave. It applies to Kubernetes targets only, since both settings act on pods.

| Setting                     | Default | What it does                                                                                                                                                           |
| --------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Clean-up Load Resources** | On      | Deletes the pods, configmaps, and secrets a run created once the run finishes. Turn it off to keep those resources for debugging, and remove them yourself afterwards. |
| **Resource Requirements**   | Off     | Sets CPU and memory requests and limits on the load pods. Turn it on when a run is throttled or evicted, or when your cluster enforces quotas.                         |

Select **Apply Changes** to keep your edits, or **Discard** to close the drawer without saving.

{% hint style="info" %}
**KEEP RESOURCES WHEN A RUN FAILS FOR NO CLEAR REASON**

If a run fails and the logs do not explain why, turn off **Clean-up Load Resources** and run it again. The pods stay in the cluster so you can inspect them with `kubectl describe` and `kubectl logs`.
{% endhint %}

***

### Save and run the test <a href="#save-and-run-the-test" id="save-and-run-the-test"></a>

1. Click **Save** to create the load test.
2. Find your test in the **Load Tests** list, which shows Type, Users, Duration, and recent executions at a glance.
3. Click the **Run** (▶) button to start an execution.
4. Monitor real-time results during execution. A breached threshold marks the run as Failed.

***

### Next steps <a href="#next-steps" id="next-steps"></a>

* Go to [Analyze load test results](/resilience-testing/load-testing/analyze-results.md) to interpret throughput, error rate, response times, and threshold outcomes.
* Go to [Python](/resilience-testing/load-testing/create-a-load-test.md) to run a Python-based load test on Linux VM or Kubernetes.
* Go to [Java](/resilience-testing/load-testing/create-a-load-test/jmeter.md) to run an existing `.jmx` test plan on Kubernetes.
* Go to [Composite load tests](/resilience-testing/load-testing/composite-load-tests.md) to run this test alongside a probe that measures health while the load is applied.
* Go to [Key concepts](/resilience-testing/load-testing/get-started.md#key-concepts) to review virtual users, load profiles, and thresholds.
