The dashboard
The live charts, the endpoint breakdown, the failed-requests panel, and starting, stopping and rescaling a run from the browser.
Three ways to get a dashboard:
loadwave demo # a self-contained tour
loadwave run test.yaml --ui # dashboard alongside a scripted run
loadwave serve # long-lived; start runs from the browserAll three serve it on :8088 by default (--ui-addr to change that). The whole
surface is also a REST API, so anything the dashboard
does is scriptable without it.
The charts
Four live charts, updating every second:
| Chart | What it shows |
|---|---|
| Virtual users | The load profile as it actually happened, which is not always the profile you asked for. |
| Throughput | Requests per second. |
| Response time | One line per endpoint, plus the aggregate percentiles. |
| Responses by status class | 2xx, 3xx, 4xx, 5xx as bands. |
Click any line in the response-time chart, or any row of the Requests table, to isolate that endpoint.
The per-endpoint lines are averages, not percentiles. A histogram per endpoint per second would cost gigabytes over an hour, where a sum and a count cost thirty-two bytes. Per-endpoint percentiles are exact for the whole run — that is what the table below the charts shows — they are simply not available second by second. See Aggregation.
Below the charts
Requests — the per-endpoint breakdown: count, average, p95, p99, max and error rate, over the whole run. These percentiles are exact, recomputed from each endpoint's merged distribution across all of its status codes.
Thresholds — every assertion with its actual value and verdict, including the ones reported as not measured.
Agents — connected nodes, their advertised capacity, their labels and their
build version. A version mismatch across a fleet shows up here immediately. Each
agent expands to show its individual worker processes, with each one's own CPU
(as a share of a core — 0.8 cores reads more usefully than 80% on a 32-core
host) and resident memory. This is the fastest way to see one worker process
starved for CPU while its siblings on the same host are not, or a host that has
run out of headroom before the metrics say so.
Events — the run's log: agents joining and leaving, quotas being recomputed, workers dying, thresholds aborting. When a result looks strange, this is where you find out that the fleet changed shape underneath it.
Failed requests — the panel that answers the question the metrics cannot.
The Failed requests panel
Metrics can tell you that 3% of requests failed. They cannot tell you they were
all 502 payment declined on one endpoint, and that is usually what you want to
know.
So each row aggregates one kind of failure — request name, method, status and transport error class — and carries a short excerpt of what the server actually said: the response body, or the transport error's text, collapsed to one line and clipped to 240 characters.
The excerpt is captured the first time each kind is seen and not again. A run where everything fails would otherwise spend its time copying error strings, and the second occurrence almost never says anything the first did not. The count keeps rising regardless.
Distinct kinds are capped, and the panel says so rather than presenting a partial list as complete.
Driving a run from the browser
With loadwave serve, runs are started, stopped and rescaled from the dashboard.
Once a run has happened, two separate buttons replace the single Start run:
- Start replays the displayed run's exact configuration immediately, with no editor in the way — the common case of "that looked fine, run it again" or "fixed the target, rerun it."
- Edit script opens the same dialog as before, seeded from that run's actual configuration rather than a blank template — in both the YAML view and the Build form, so switching between them shows the real scenario either way.
If the run came from a file — loadwave run test.yaml --ui — editing it in the
dialog autosaves back to that file, a moment after you stop typing.
Editing a run with no file behind it (a Go scenario, or one started directly
from the dashboard) still lets you change it and start a new run from the
result; there is simply nowhere on disk for those edits to land.
Rescaling takes a ramp. Raising the target introduces the new virtual users over a period rather than spawning them all in one tick — a step change in concurrency measures the step, not the service.
Stopping a run does not stop LoadWave. The run ends, the results stay on screen, and you can download the report or start another test. Taking away the results at the moment somebody wants to read them would be a strange thing to do.
Ending the process is a separate, deliberate action: Ctrl-C in the terminal, or
the Power off control in the browser — which only appears when the server was
started with --allow-shutdown.
Runs are sequential: one at a time. Concurrent runs would share worker processes and network capacity, and the resulting numbers would measure the interference rather than the system under test.
Two flags worth knowing
loadwave serve --read-only # dashboard only; no starting or stopping runs
loadwave serve --allow-shutdown # expose the browser's Power off control--read-only is right when showing a run to an audience, or when more people can
reach the dashboard than should be able to start load against production.
Neither flag is a security boundary. The control plane is unauthenticated and
unencrypted — anyone who can reach the dashboard port can start and stop runs
unless --read-only is set, and anyone who can reach the agent port can join the
fleet and receive test plans, which may contain credentials. Run it on a trusted
network. See Securing it.
Browser origins
The live stream is a WebSocket, and its origin check is strict by default. If you reach the dashboard through a reverse proxy on a different hostname, tell the server about it:
loadwave serve --allowed-origin https://loadwave.internal.example.comTaking the results away
The Download report button, or:
loadwave run test.yaml --report results.htmlEither way you get one self-contained HTML file — see Reports.