Skip to content

Configuring Workloads

A workload is a pool of ephemeral runners attached to one GitHub organization or personal account. It holds the targeting, sizing, runtime, cache key, and host pool; the runner’s shape lives in a separate runner profile it references. Create a workload from the Add workload dialog; edit it later from /workloads/<id>.

The connected GitHub organization or personal account this pool serves. The account must already be added with a valid classic PAT — see the quickstart. One account can back many workloads; one workload serves exactly one account.

The runner profile this workload uses for the runner’s shape — image, memory and CPU limits, custom env, pull policy, and an optional registry credential. A profile is reusable: many workloads can point at the same one, so you define the shape once. Editing a profile applies to each referencing workload on its next reconcile pass (new spawns only).

Runaway sizes the pool to demand between two bounds:

  • minRunners — the warm floor kept ready even when the queue is empty.

  • maxRunners — the ceiling Runaway scales up to under load.

  • Scale to zerominRunners = 0. No runners sit idle; the pool spins up when work arrives.

  • Warm poolminRunners = N keeps N runners ready, trading idle capacity for lower start latency.

  • Fixed poolminRunners = maxRunners pins the pool to a constant size.

For how demand is measured, see Autoscaling.

GitHub routes a job to a runner when the job’s runs-on labels are a subset of the runner’s labels. Give each workload a distinct label and match it in your workflow:

jobs:
build:
runs-on: [self-hosted, my-label]

Each workload runs on exactly one runtime tier. The tier decides how much Docker access, and how much isolation, its runners get.

| Tier | Docker access | Isolation | Host requirement | |---|---|---|---| | none (default) | None inside runners | n/a | None | | shared-daemon | Shares the host daemon via a bind-mounted socket (DooD) | None | None | | dind | Private privileged inner daemon per runner | Process-level only | None | | isolated-sysbox | Private nested daemon per runner (user-namespaced) | Strong | sysbox-runc installed | | isolated-kata | Private daemon in a lightweight VM with its own kernel | Strongest | kata runtime + /dev/kvm |

  • none is the safe default — runners have no Docker access at all.
  • shared-daemon gives maximum cache reuse but no isolation. GitHub container: and services: jobs do not work here.
  • dind works anywhere and supports container: / services:, but privileged means a container escape is host-root. It requires an explicit acknowledgement and is only for trusted, single-tenant CI.
  • isolated-sysbox and isolated-kata give each runner its own nested Docker daemon with real isolation.

See Isolated runtime for the nested tiers and their host setup.

A workload selects a host-owned cache volume with an optional cache key. With no key, all same-org workloads on a host share one organization cache; with a key, they get a separate warm cache. Different organizations never share a cache. See Caching for how the volume is resolved.

Assign the workload to one or more enrolled hosts from Edit hosts. The scheduler distributes runners across every targeted host, bounded by a per-host cap and by each host’s shared runner-capacity budget. See Running multiple hosts.