LoadBolt auto-generates scripts for simple tests. When you need more control, here's how to write your own.
Every test in LoadBolt has a k6 script behind it. When you create a test with just a URL, LoadBolt generates a simple script that hits that URL. You can switch to the script editor at any time to customize the script or paste one you've written locally.
export default function — that's how k6 knows what to run. Max file size is 512 KB.A k6 script is plain JavaScript. Here's the minimal version:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
const res = http.get("https://api.yourapp.com/health");
check(res, { "status is 200": (r) => r.status === 200 });
sleep(1);
}Each virtual user runs this function in a loop for the duration of the test. The check() call verifies the response, and sleep(1) pauses for 1 second between iterations to simulate realistic pacing.
LoadBolt lets you define variables in the UI (key-value pairs). Behind the scenes, these get passed as --env KEY=VALUE flags to k6. In your script, access them with __ENV.KEY:
import http from "k6/http";
export default function () {
const baseUrl = __ENV.BASE_URL;
const apiKey = __ENV.API_KEY;
http.get(`${baseUrl}/data`, {
headers: { "X-API-Key": apiKey },
});
}This keeps secrets and environment-specific values out of your script code. Set BASE_URL to your staging server for testing, swap it to production later.
Most real-world tests involve authentication. Here's a pattern that logs in, grabs a token, and uses it for subsequent requests:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
// Log in
const loginRes = http.post(
`${__ENV.BASE_URL}/api/login`,
JSON.stringify({
email: __ENV.TEST_EMAIL,
password: __ENV.TEST_PASSWORD,
}),
{ headers: { "Content-Type": "application/json" } }
);
check(loginRes, { "login succeeded": (r) => r.status === 200 });
const token = loginRes.json("token");
// Hit an authenticated endpoint
const dashRes = http.get(`${__ENV.BASE_URL}/api/dashboard`, {
headers: { Authorization: `Bearer ${token}` },
});
check(dashRes, { "dashboard 200": (r) => r.status === 200 });
sleep(1);
}A single script can hit as many URLs as you want. Each virtual user will run through all of them in sequence:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
check(http.get(`${__ENV.BASE_URL}/`), {
"homepage 200": (r) => r.status === 200,
});
sleep(0.5);
check(http.get(`${__ENV.BASE_URL}/api/products`), {
"products 200": (r) => r.status === 200,
});
sleep(0.5);
check(http.get(`${__ENV.BASE_URL}/api/products/1`), {
"product detail 200": (r) => r.status === 200,
});
sleep(1);
}LoadBolt's results page breaks down metrics per endpoint, so you can see exactly which routes are slow.
If you want each virtual user to use different data (different user IDs, search terms, etc.), use an array or k6's SharedArray:
import http from "k6/http";
import { SharedArray } from "k6/data";
const userIds = new SharedArray("userIds", function () {
return [101, 102, 103, 104, 105, 106, 107, 108, 109, 110];
});
export default function () {
const userId = userIds[__VU % userIds.length];
http.get(`${__ENV.BASE_URL}/api/users/${userId}`);
}SharedArray is memory-efficient — the data is shared across all VUs instead of being copied for each one. Good practice for larger datasets.
Real users don't fire requests as fast as possible. Adding sleep() between actions simulates realistic behavior and gives you more meaningful results:
import http from "k6/http";
import { sleep } from "k6";
export default function () {
http.get(`${__ENV.BASE_URL}/`);
sleep(Math.random() * 3 + 1); // 1-4 seconds, randomized
http.get(`${__ENV.BASE_URL}/api/search?q=test`);
sleep(Math.random() * 2 + 0.5); // 0.5-2.5 seconds
}Without sleep, each VU will hammer your server as fast as the network allows. That's a stress test, not a realistic load test. Both are useful — just know which one you're running.
check() lets you define pass/fail conditions. LoadBolt tracks check pass rates in your results:
import http from "k6/http";
import { check } from "k6";
export default function () {
const res = http.get(`${__ENV.BASE_URL}/api/health`);
check(res, {
"status is 200": (r) => r.status === 200,
"response time < 500ms": (r) => r.timings.duration < 500,
});
}Failed checks don't stop the test — they just show up as failures in your results. This is how you catch things like “the API returns 200 but gets slow under load.”
options.vus, options.duration, or options.stages, LoadBolt will override them with the values you set in the UI. This is intentional — it means you can reuse the same script with different load profiles without editing it.LoadBolt runs k6 v0.55.0 under the hood. Everything you can do in k6, you can do in LoadBolt. The official k6 docs are excellent if you want to go deeper — modules, protocols, custom metrics, and more.