Skip to content

Enabling Webhooks

Runaway scales by polling GitHub for queued work, which needs no public exposure and works behind NAT. Webhooks are an optional, per-org overlay: they give the hub a lower-latency demand signal so it reacts to a queued job faster. Polling stays as the automatic fallback — if webhook delivery goes quiet or a health check trips, the hub resumes polling for that org on its own.

For the difference between the two channels, see Webhooks vs. polling.

The org’s PAT needs admin:org_hook in addition to the usual repo and admin:org scopes. Without it, the Enable button is disabled and explains the missing scope. See GitHub setup for how to mint a token with the right scopes.

Turn webhooks on from either of two places.

The Add Organization dialog has an “Enable GitHub webhooks for faster scaling” checkbox. It’s checked by default when the validated PAT has the right scope.

Either way, the hub mints a random secret, creates the GitHub-side workflow_job webhook, and stores the secret encrypted at rest.

Behind a reverse proxy with split hostnames

Section titled “Behind a reverse proxy with split hostnames”

If the public origin GitHub should deliver to differs from your sign-in origin, set RUNAWAY_PUBLIC_URL to the public webhook-ingress origin. Single-origin deploys can leave it unset — the hub falls back to your auth URL. See TLS and reverse proxy.

An out-of-band poller asks GitHub about the hook directly and surfaces breakage on the org page when it finds a definitive fault:

  • the hook was deleted,
  • GitHub auto-disabled it,
  • it lost its workflow_job subscription, or
  • recent deliveries are failing.

Transient GitHub-side hiccups (a network blip, a 5xx) don’t flip the state — only a definitive answer does. When a hook is broken, a Repair button does a sequential disable then re-enable and rotates the secret. Repair is always something you trigger; the hub never deletes and recreates a webhook on its own.

GitHub delivers webhooks to a public URL, so a dev hub that isn’t reachable from the internet needs a tunnel. None of these are bundled — pick one and point your public URL at it:

Terminal window
# GitHub CLI's official forwarder — no external service
gh webhook forward --repo <owner>/<repo> --events workflow_job \
--url http://localhost:3000/api/webhooks/github
  • smee.io — paste the forwarded URL into RUNAWAY_PUBLIC_URL before enabling.
  • cloudflared tunnel — a durable public URL, handy when the dev environment outlives a single gh webhook forward session.

Point your public URL at the tunnel, then enable webhooks for the org. The hub doesn’t care which tunnel runs in front of it.