First run
This guide assumes you want the fastest path to proof that test-scale works on your machine. Later sections explain how to aim workers at your own API.
Prerequisites
Section titled “Prerequisites”| Requirement | Why |
|---|---|
| Go 1.26.5+ | Builds control plane, worker, and aggregator images during make quickstart |
| Docker Compose v2 | Runs Postgres, NATS, Prometheus, Grafana, workers |
curl, jq, Git |
Health checks and smoke script |
Ensure the Docker daemon is running before you start.
Step 1 — First success (make quickstart)
Section titled “Step 1 — First success (make quickstart)”git clone https://github.com/MohamedBenTaher/test-scale.gitcd test-scalemake quickstartWhat this does, in order:
- Preflight — checks
docker,curl,jq, andgo. - Build — rebuilds
ctrl-plane,worker, andaggregatorimages from your checkout (avoids stale auth/payload bugs). - Start stack — Postgres, NATS, mock target, Prometheus, Grafana, autoscaler.
- Health wait — control plane
/pingand dependencies. - Smoke — submits
examples/smoke.yaml(~10 RPS, 1 worker, bundled mock target).
Success looks like:
PASS: run run-0711-2042-abcd completed with 1 worker(s), 10 RPS, 0 errorsFirst run complete. UI: http://localhost:8080 Grafana: http://localhost:3001If host ports 8080 or 3001 are busy, test-scale picks free ports and writes them to deploy/docker/.ports.env. Always use the URLs printed at the end, or run source deploy/docker/scripts/ports.sh before using the CLI directly.
Step 1b — Open the web UI
Section titled “Step 1b — Open the web UI”After quickstart succeeds:
make docker-openThe embedded UI lets you submit plans, watch runs, compare baselines, and open Grafana without YAML. Full walkthrough: Web UI.
Step 2 — Run another plan
Section titled “Step 2 — Run another plan”make docker-run PLAN=examples/http-post.yamlmake docker-openmake docker-run loads the API URL, API key, and ports for you. When calling the CLI yourself:
source deploy/docker/scripts/ports.sh./bin/test-scale run -f examples/smoke.yamlStep 3 — Hit an app on your laptop
Section titled “Step 3 — Hit an app on your laptop”Workers run inside Docker. localhost inside a container is the container itself, not your Mac or Linux host.
| Target location | target_url host |
|---|---|
| Bundled mock (Compose network) | http://mock-target |
| App on your machine | http://host.docker.internal:<port> |
| Remote staging/prod | Normal hostname or IP |
Example:
target_url: http://host.docker.internal:8000/healthtarget_rps: 20duration_sec: 10Your app must listen on an interface reachable from Docker (not only 127.0.0.1 in some setups).
Step 4 — Protected routes and secrets
Section titled “Step 4 — Protected routes and secrets”Never commit live tokens. Plan files support ${ENV_VAR} expansion when the CLI parses YAML (before the plan is stored).
export TOURBA_JWT='eyJ...'make docker-run PLAN=examples/http-auth-jwt.yamlmethod: GETauth: type: jwt token: ${TOURBA_JWT}Auth type |
Effect |
|---|---|
bearer / jwt |
Sets Authorization: Bearer <token> |
session / cookie |
Sets Cookie: <name>=<value> |
header |
Sets a custom header name/value |
Debug 401s: use curl with the same header first. “Credentials not provided” means the worker did not send auth (stale worker image or plan not re-submitted). “Invalid token” means auth arrived but the target rejected it.
Plans persist resolved headers in the database. Restrict who can read plans and rotate test credentials.
Step 5 — Request bodies
Section titled “Step 5 — Request bodies”Structured JSON (preferred):
method: POSTjson: query: health limit: 10expected_statuses: [200, 202]Raw body:
method: POSTbody: '{"query":"health"}'headers: Content-Type: application/jsonDo not set both json and body.
Step 6 — Workers and RPS
Section titled “Step 6 — Workers and RPS”target_rps is the total throughput for the run, not per worker.
Default worker count:
ceil(target_rps / autoscaler.per_worker_rps)Default capacity is 500 RPS per worker. Example: target_rps: 2000 → 4 workers.
Override when you want more connections at a lower rate per worker:
target_rps: 40worker_count: 4That splits ~10 RPS per worker; it does not mean 40 RPS each.
Docker workers still share one host. For multi-node generators, use Kubernetes + KEDA.
Step 7 — Read the result
Section titled “Step 7 — Read the result”| Field | Meaning |
|---|---|
rps |
Achieved aggregate throughput |
p50_ms, p95_ms, p99_ms |
Latency percentiles (histogram-merged) |
error_count |
Timeouts, connection failures, statuses outside expected_statuses |
worker_count |
Workers assigned to the run |
completed with error_count > 0 still finished — treat errors as a failed test for gates.
Lifecycle commands
Section titled “Lifecycle commands”make docker-down # stop stack, keep Postgres volumemake docker-up # restart and rebuild app imagesmake docker-reset # destroy volumes and all run history (local only)Schema upgrades use versioned SQL migrations; you should not need docker-reset after a version bump.
Troubleshooting
Section titled “Troubleshooting”| Symptom | What to do |
|---|---|
| Docker daemon not reachable | Start Docker Desktop/Engine |
Missing jq, curl, or Go |
Install the tool named in the error |
valid Test Scale API key is required |
Use make docker-run or source deploy/docker/scripts/ports.sh |
| Control plane unreachable | make docker-up; use printed URL |
| Host target connection refused | host.docker.internal; check bind address |
| All requests 401 | Verify token with curl; make docker-up to rebuild workers; submit a new plan |
failed to create plan after upgrade |
Check ctrl-plane logs; restart so migrations run |
| RPS below target | Check errors and CPU; add workers or lower per_worker_rps |
Diagnostic acceptance run:
make docker-verifyNext: Example plans and the CLI.