How does GitHub decide which self-hosted runners to use?

We have multiple self-hosted runners across a few locally hosted Macs, with various custom labels. How does GitHub decide where a job is run, when it matches multiple runners in the list? At first, I thought it would just choose the first runner that matches in the list of runners in the GitHub settings UI (so, in order of when the runner was added to GitHub), but that doesn’t appear to be the case. Does it somehow try to load balance across devices, or based on CPU load?


Hi @grant-mccarriagher,

Thanks for you reaching this out!

No, there is no load balance, GitHub will choose a runner that matches the label requirements, but indeed there is Routing precedence for self-hosted runners:

If you use both repository-level and organization-level runners, GitHub follows an order of precedence when routing jobs to self-hosted runners:

  1. The job’s runs-on labels are processed. GitHub then attempts to locate a runner that matches the label requirements:

  2. The job is sent to a repository-level runner that matches the job labels. If no repository-level runner is available (either busy, offline, or no matching labels):

  3. The job is sent to an organization-level runner that matches the job labels. If no organization-level runner is available, the job request fails with an error.


Thanks for the reply. But when multiple self-hosted runners match the requested labels, how does GitHub decide which one is used? Is it random? Like I said in the original post, it doesn’t appear to be related to order.

Hi @grant-mccarriagher,

Thanks for your reply!

If they are all repo-level runners, Github will choose free&online runner for the workflow. Checked on my side, it will pick the first free runner from the list, but i don’t find the official doc about it.
Are your runners are all repo-level? And they are picked randomly?


Yes, they are all org-level runners.

Well, it doesn’t appear to be totally random, but it’s not in list order. For example, in this photo, if two runners are requested, both with the labels “self-hosted” and “ios”, the runner “ios-runner-1-mac-mini-4” will be used, and then “ios-runner-1-mac-mini-1” will be used.

One is at the very top of the list, the other is towards the middle.

Hi @grant-mccarriagher,

What i checked on my side is the first two runners will be picked for workflow. I’m confirming with Github and will update once there’s a response.