Skip to content

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.

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.

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_hook scope on that org’s PAT so Runaway can create the webhook.

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.