Runner group
Included from Basic tier
A runner group is the placement unit of the dispatch plane: a named set of runners carrying the admin-managed attributes selectors match against, the node-path bindings that zone work, and the exclusive flag that refuses untargeted overflow.
The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.
Endpoints (all admin-token gated):
GET /api/runner_groups—{"groups": [...]}, the stored group rows serialized directly from the registry (runnerrpc.RunnerGroup).PUT /api/runner_groups/{name}— upsert; answers the stored group.DELETE /api/runner_groups/{name}— answers{"name", "deleted": true}; refused 409 while any registered runner/agent or still-redeemable registration token references the group.POST /api/runner_groups/{name}/drain//resume— answer{"name", "draining": <bool>}.GET /api/runner_groups/{name}/demand— the autoscaler signal:{"group", "queued", "running"}.
Group fields
| Field | Type | Meaning |
|---|---|---|
name | string | The group name — letters, digits, ., _, - only (ValidateGroupName). Creating a new name is gated by the licence's runner_groups limit; joining an existing group never refuses. |
description | string | Free-text description from the upsert body. Absent when empty. |
exclusive | bool | An exclusive group's runners only ever receive work targeted at the group (by name, binding, or matching selectors) — never unconstrained overflow. Absent when false. |
attributes | string map | The group's placement attributes (key → value). Keys must be registered in the attribute registry (GET /api/runner_attributes), and a key with enumerated values only accepts one of them — write-time validation. Absent when empty. |
bindings | string array | Node-path prefixes (each must be an absolute path starting /): work targeting a bound subtree is offered only to bound groups' runners. Absent when empty. |
drain_requested_at | timestamp | When a group-wide drain was requested; its presence is the drain flag. Preserved across upserts. Absent when not draining. |
halted_at | timestamp | When an emergency halt of the group was applied (the per-group scalpel, used on the agent plane). Absent when not halted. |
halted_by | string | Who applied the halt. Absent when not halted. |
halt_reason | string | The halt's stated reason. Absent when not halted. |
created_at | timestamp | When the group row was created — including auto-vivification: minting a registration token for a group that does not exist creates the row (EnsureGroup). |
updated_at | timestamp | Last upsert time. |
Demand payload (GET /api/runner_groups/{name}/demand)
| Field | Type | Meaning |
|---|---|---|
group | string | The group name. |
queued | int | Unleased queue entries whose placement requirement this group's posture (name, attributes, exclusive flag) could satisfy. |
running | int | Live job bindings currently held by the group's member runners. |
What changes it
PUT /api/runner_groups/{name}replaces description, attributes, bindings and exclusive wholesale (drain state is preserved).- Token minting auto-vivifies a missing group row with defaults.
- Group drain/resume set and clear
drain_requested_at(eventinfo:runner:group_drain_changed); delete emitsinfo:runner:group_deleted. - The refusal on delete names what still references the group, verbatim:
runner group "<name>" is still referenced by N registered runner(s)/agent(s) — drain and stop them, then 'opschain admin runner forget', and N still-redeemable registration token(s) — let them expire or exhaust before deleting the group.
Related pages
opschain docs fleet-queue— how groups, attributes and bindings decide placement verdicts.opschain docs runner-registry— runner membership and liveness.opschain docs entitlements— therunner_groupslicence limit on new names.