Skip to content
Latchkey
Documentation menu

How provisioning works

What happens between a job queueing on GitHub and a runner picking it up: warm pools, cold starts with just-in-time registration, the concurrency ceiling, and why a job might wait.

When GitHub queues a job with a latchkey-* label, a webhook tells Latchkey immediately, and Latchkey makes one decision: hand the job to a runner that is already warm, or launch a fresh machine for it. You never see that decision, but it is the difference between a pickup in a few seconds and one in about ten, and it explains most of what this page covers.

01Job queuedGitHub queues a job targeting a latchkey-* label
02WebhookGitHub notifies Latchkey the moment the job queues
03Scale-up decisionA warm runner takes the job, or a fresh machine launches
04PickupWarm: a few seconds. Cold: about 10 seconds, registered just-in-time
05Run and destroyThe job runs alone on the machine, which is terminated afterward
Seconds
warm pickup
when a warm runner is available
~10s
cold start
fresh machine, just-in-time registration
1
job per VM
every runner is destroyed after its job
4h
job cap
the machine is terminated at 4 hours

Warm pickup vs cold start#

A warm pickup means a pre-provisioned runner was already online for your workspace: the job is handed straight to it and starts within a few seconds. A cold start means no warm runner fit, so a fresh machine boots just for that job, registers with GitHub using a just-in-time, single-use configuration, and is running your steps in about 10 seconds. Both paths end identically: the runner takes exactly one job and the machine is terminated when the job finishes.

Warm pools#

PlanWarm pool
DeveloperOne warm latchkey-small runner plus parked capacity
LaunchThe same baseline: one warm runner plus parked capacity
ScaleThe same baseline: one warm runner plus parked capacity

Every plan gets the same warm baseline today, and it covers latchkey-small only: one always-on warm runner backed by parked machines that resume in seconds. Anything beyond that, and any job asking for a larger size, cold-starts in about 10 seconds. Warm capacity costs you nothing while it idles; billing is per job minute, and an idle warm runner is not running a job.

Ephemeral, always#

Provisioning never reuses a machine. Warm or cold, a runner takes exactly one job and is terminated afterward, along with its disk, which is why runner names in the GitHub run view change on every run and why anything a job writes to local disk is gone when the job ends. The security side of this design, just-in-time credentials, private networking, and encrypted single-use disks, is covered in Security architecture.

Concurrency#

A workspace runs up to 20 concurrently busy runners by default, and only busy runners count: idle warm runners never consume a slot, so warm capacity does not compete with your real jobs. When a burst needs more than 20 at once, the overflow jobs stay queued until a slot frees, then start on their own; nothing errors and nothing is lost. If your peaks queue regularly, higher limits are available, contact support.

Hard boundaries#

  • Jobs are capped at 4 hours; the machine is terminated at 4 hours even if the job is still running.
  • Runners are Ubuntu 24.04 LTS on x86_64 only: no Windows, macOS, arm64, or GPU hosts.
  • Runners run in AWS us-east-1.

The full table, including disk sizes and per-plan custom configuration counts, is in Limits and concurrency.

Why a job might sit in queued#

When provisioning cannot or will not launch a runner, there is no error on GitHub's side; the job just waits. That is by design: jobs targeting latchkey-* labels stay queued on GitHub rather than erroring. The usual causes:

  • A label typo: no configuration matches the label, so nothing ever picks the job up.
  • The repository is not monitored, or the runner configuration is disabled.
  • A billing or trial block: an expired trial, a lapsed subscription, or an exhausted free tier on a card-less trial. A "Managed runner blocked" notification fires when this happens, at most once per day.
  • A custom runner's image is still building; jobs targeting its label wait until the build completes.
  • The workspace is at its 20-busy-runner ceiling; the job starts as soon as a slot frees.

The symptom-by-symptom walkthrough, with how to confirm each cause, is in Troubleshooting.

References