Skip to main content
Version: v5.7.0

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​

FieldTypeMeaning
namestringThe 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.
descriptionstringFree-text description from the upsert body. Absent when empty.
exclusiveboolAn exclusive group's runners only ever receive work targeted at the group (by name, binding, or matching selectors) — never unconstrained overflow. Absent when false.
attributesstring mapThe 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.
bindingsstring arrayNode-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_attimestampWhen a group-wide drain was requested; its presence is the drain flag. Preserved across upserts. Absent when not draining.
halted_attimestampWhen an emergency halt of the group was applied (the per-group scalpel, used on the agent plane). Absent when not halted.
halted_bystringWho applied the halt. Absent when not halted.
halt_reasonstringThe halt's stated reason. Absent when not halted.
created_attimestampWhen the group row was created — including auto-vivification: minting a registration token for a group that does not exist creates the row (EnsureGroup).
updated_attimestampLast upsert time.

Demand payload (GET /api/runner_groups/{name}/demand)​

FieldTypeMeaning
groupstringThe group name.
queuedintUnleased queue entries whose placement requirement this group's posture (name, attributes, exclusive flag) could satisfy.
runningintLive 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 (event info:runner:group_drain_changed); delete emits info: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.
  • opschain docs fleet-queue — how groups, attributes and bindings decide placement verdicts.
  • opschain docs runner-registry — runner membership and liveness.
  • opschain docs entitlements — the runner_groups licence limit on new names.