What you will do
- The control plane generates the install. You never assemble runner identity, service tokens, or TLS material by hand.
GET /v1/system/dispatcher/runner-bootstrap-commandreturns a one-time command; its token expires in 10 minutes and is consumed by the first successful download.- Query parameters choose identity and placement:
runner_id,runner_name,runner_scopes,runner_capacity,runner_network_mode(bridge,host, orauto), andrunner_image. - In bridge mode the generated
docker runjoins the network named byDOCKER_NETWORK_NAME, which isnopsai-netfor the local stack. - The container runs with
--restart always, mounts/var/run/docker.sock, and carriesRUNNER_ID,RUNNER_SCOPES,RUNNER_CAPACITY,DISPATCHER_GRPC_ADDRESS,NOPSAI_API_URL, and the service JWT and TLS values as environment. - An explicitly empty
runner_scopesvalue means all scopes; omitting the parameter falls back to the configured default, then toprod. - The install script stops early when the runner image cannot be pulled, or when the image architecture does not match the Docker host.
Before you start
- Control plane
- A running API that has completed first-install setup
curl -s localhost:8080/healthz - Token
- An administrator token in
NOPSAI_TOKENcurl -s -H "Authorization: Bearer $NOPSAI_TOKEN" localhost:8080/v1/auth/me - Runner host
- A host with Docker and access to
/var/run/docker.sockdocker info --format "{{.Architecture}}" - Runner image
- The runner image tag available locally or pullable on that host
docker image inspect ghcr.io/nopsai/nopsai-docker-runner:dev >/dev/null && echo present
Steps
- 01
Generate the install command
Ask the control plane for a bootstrap command for the runner you want. Scopes decide which work this runner may be given; capacity decides how many runs it accepts at once.
Important- The command expires 10 minutes after it is generated and is consumed by the first successful download. Generate a new one per host.
Request a bootstrap commandbash curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" \ "http://localhost:8080/v1/system/dispatcher/runner-bootstrap-command?runner_id=runner-local-1&runner_scopes=prod&runner_capacity=2&runner_network_mode=bridge" \ | jq -r .bootstrap_command - 02
Run it on the runner host
Paste the generated command on the Docker host that will execute pipeline work. For the local stack, that is the same workstation running Compose.
Expected result- The script prints the runner ID, dispatcher address, and network mode, pulls the runner image if it is missing, starts the container, and tails its first log lines.
Verify- docker ps --filter "label=nopsai.io/runner-id" --format "{{.Names}} {{.Status}}"
- 03
Confirm the runner is on the same network
A bridge-mode runner must sit on
nopsai-netto reach the dispatcher by name. This is the failure most often mistaken for a credentials problem.List attached containersbash docker network inspect nopsai-net --format '{{range .Containers}}{{.Name}}\n{{end}}'Verify- The runner container appears in the list alongside the control-plane containers.
- 04
Confirm registration with the dispatcher
Registration is what makes the runner dispatchable. Check it from the API or from System, then the dispatcher workspace.
Read dispatcher statusbash curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" http://localhost:8080/v1/system/dispatcher | jqExpected result- The runner appears with its ID, scopes, and capacity, reachable and dispatch-enabled.
Verify- If it does not appear, follow the container logs:
docker logs -f <runner container>.
How it works
Prefer the bootstrap command over hand-written Compose for a first runner: it carries the dispatcher address, service JWT settings, and TLS material that the control plane currently considers correct, and it fails loudly when one of them is not configured.
GET /v1/system/dispatcher/runner-compose returns the same install as a Compose service fragment when you would rather keep the runner in a Compose file than as a standalone container.
When the dispatcher address in the response is not reachable from the runner host, fix the address before installing. The generated command reports the address it will use, and the response carries warnings when the control plane had to derive it from the request.
Host network mode exists for the case where the host can reach the dispatcher but bridge containers cannot. It is the right answer on some VM setups and the wrong answer on a normal local install.
Limits
- One bootstrap command installs one runner. A second host needs its own command and its own runner ID.
Implementation evidence
services/nopsai/internal/runnerinstall/docker.goInstall spec, generated docker run, network placement, and one-time token behavior.
services/nopsai/routes.goDispatcher install and status routes.

