Autoscaling: polling and webhooks
Runaway sizes each workload to demand, the count of busy plus queued jobs. It learns that demand through two channels: polling, which runs by default, and webhooks, which you can opt into per organization.
Polling — the universal channel
Section titled “Polling — the universal channel”By default Runaway polls GitHub roughly every 30 seconds for each org’s queued and in-progress jobs, and sizes the pool to match. Polling works everywhere with no setup:
- No public exposure. The hub reaches out to GitHub; GitHub never has to reach the hub. A NAT’d homelab hub autoscales out of the box.
- Near-zero rate-limit cost. Requests are ETag-cached, so an idle org costs almost nothing against your GitHub API budget.
For most homelab and small-team workloads, a ~30-second reaction time is fine.
Webhooks — the lower-latency overlay
Section titled “Webhooks — the lower-latency overlay”If you want runners to react faster than the poll interval, you can enable a
per-org workflow_job webhook. When GitHub queues a job it tells the hub
immediately, instead of the hub waiting for its next poll. Webhooks are an
opt-in overlay, not a replacement, and they ask for more from your setup:
- A publicly reachable hub so GitHub can deliver the webhook.
- The
admin:org_hookscope on that org’s PAT so Runaway can create the webhook.
Polling resumes if a webhook breaks
Section titled “Polling resumes if a webhook breaks”Webhooks aren’t a one-way door. When an org has healthy webhook delivery the
autoscaler reads demand from it and stops polling that org. If a periodic health
check finds the hook broken — deleted, disabled, missing its workflow_job
subscription, or failing deliveries — the org falls back to polling on its own,
with no action from you. Both channels count the same thing, busy plus queued
jobs, so the fallback changes the latency, not how many runners you get.