---
title: Runners
description: A Runner is one machine for exactly one job, on its own Sandboxes lease; a ScaleSet registers one runner class on one Sylphx Runners installation.
type: tutorial
product: runners
summary: What a runner is, and what a scale set registers.
updated: 2026-10-01
order: 0
---

<Callout tone="note" title="Today this runs on the management API">
Self-hosted runners are registered with `POST /v1/runners` and read with `GET /v1/runners` on the management API; the [Runners quickstart](/docs/runners/quickstart) shows them. The `scale_sets` and `runners` collections in the [API reference](/docs/api/scale_sets) are not served at api.sylphx.com.
</Callout>

A Runner is one runner: one machine for exactly one job, on its own Sandboxes
lease, released and wiped after. Nothing carries over from one job to the
next, and Runners is its only writer — a caller reads a runner, never creates
one.

Today Runners runs GitHub Actions jobs for customer repositories: a workflow
that asks for one of our classes is answered with a machine from here.

## A scale set is the registration

A [ScaleSet](/docs/api/scale_sets) registers one runner class on one Sylphx
Runners installation. That registration is what the forge holds and what a job
is matched against. Runners holds the message session between the installation
and itself with one fenced holder, so the session is delivered to one holder
and not two.

GitHub matches jobs to its runners; Runners keeps no global queue of its own.
There is no list of pending work on our side that could disagree with the
forge about what is waiting.

## The parts you set

<PropertyTable
	properties={[
		{
			name: 'connection',
			type: 'string',
			description: 'The Sylphx Runners installation Connection. Required.',
		},
		{
			name: 'runner_class',
			type: 'string',
			description: 'The class, sylphx-<os>-<size>, for example sylphx-linux-standard. It is the label a workflow asks for. Required.',
		},
		{
			name: 'runner_group',
			type: 'string',
			description: 'The forge runner group to register in; the installation’s default group by default.',
		},
		{
			name: 'max_runners',
			type: 'int32',
			description: 'Concurrent runners at most; 0 means the plan’s limit.',
		},
	]}
/>

Everything else on a scale set is observed rather than set: whether the
registration is Ready, how many of its runners are busy and how many are idle,
and the region whose holder owns the message session. [Scale sets](/docs/runners/scale-sets)
covers those, and [Runner lifecycle](/docs/runners/runner-lifecycle) covers the
machine that a single job gets.

## Every name is a path

A scale set and its runners are addressable under one name, and the same name
works in the API, the CLI and the console:

<KeyValue
	items={[
		{ key: 'Scale set', value: 'orgs/{org}/scale_sets/{scale_set}', mono: true },
		{ key: 'Scale set id', value: 'rss_<cell><ulid>', mono: true },
		{ key: 'Runner', value: 'orgs/{org}/scale_sets/{scale_set}/runners/{runner}', mono: true },
		{ key: 'Runner id', value: 'rnr_<cell><ulid>', mono: true },
	]}
/>

An id is never reused. `runners:read` covers reading a scale set and its
runners; everything that changes a scale set needs `runners:write`.

<RelatedDocs
	links={[
		{
			href: '/docs/runners/quickstart',
			label: 'Quickstart',
			description: 'Register a scale set, then read its runners back.',
		},
		{
			href: '/docs/api/scale_sets',
			label: 'The scale_sets collection',
			description: 'Every method, its scope and its examples.',
		},
		{
			href: '/docs/api/runners',
			label: 'The runners collection',
			description: 'What a runner carries, and how to read one.',
		},
		{
			href: '/docs/cli/scale_sets',
			label: 'The CLI commands',
			description: 'The same calls as sylphx runners scale-sets.',
		},
	]}
/>
