Runner groups are an organization policy that decides which repositories can send jobs to a set of self-hosted runners. Without them, every runner lands in one default group open to the whole organization. Create groups per trust level, assign only the repositories that need each, and keep public repositories off.
What GitHub documents
From GitHub's guidance on managing access to self-hosted runners:
- You can use policies to limit access to self-hosted runners added to an organization.
- A group can be accessible to a specific list of repositories, or to all repositories in the organization.
- If no group is given at registration, runners join the default group.
- By default, only private repositories can access runners in a group, though you can override this. GitHub recommends using self-hosted runners only with private repositories.
- Organization owners on the GitHub Team plan can create additional organization-level runner groups. Every organization has a default group.
The security guidance adds the reason: organization-level runners can serve multiple repositories, and groups limit the impact of a compromise.
Why the default group is a risk
A runner in the default group can run a job from any repository the policy allows, and the policy is broad unless you narrow it. The job that arrives is code from a workflow file. Anyone who can change a workflow file in an allowed repository can run commands on that machine. If the runner is persistent, what they leave behind can affect the next job from a different repository.
A grouping scheme that works
| Group | Repositories | What the runners hold |
|---|---|---|
| Build | Application repositories, private | Build tools, read access to code |
| Deploy | A short list of release repositories | Network path to production, deploy roles |
| Experimental | Small projects and trials | Nothing sensitive, disposable |
The point is that a compromise in the experimental group cannot reach the deploy group's machines, because the two groups share no runner and no repository access.
Steps to tighten access
- List your runners and which group each is in. Anything in the default group is a candidate to move.
- Define groups by trust level, not by team. Deployment capability is the dividing line that matters most.
- For each group, choose selected repositories rather than all repositories.
- Confirm that no public repository can use a group unless you made that choice on purpose and the runners are ephemeral and isolated.
- Where your plan allows, restrict by workflow as well as repository, using the policy options GitHub provides for the group.
- Prefer one job per runner and automatic removal afterward. GitHub describes just in time runners as performing at most one job before being removed, though reused hardware can still expose information unless the environment is reset.
- Record the grouping in your security documentation, so audits can see the intent.
What groups do not do
Groups restrict which repositories reach a runner. They do not make the code those repositories run safe, and they do not isolate two jobs that land on the same machine in the same group. For that you need ephemeral runners or isolation per job. For the related topics, see self-hosted GitHub runners security risks and ephemeral vs persistent runners.
Quick check
Should public repositories use self-hosted runners?
GitHub recommends using self-hosted runners only with private repositories. Public repositories accept pull requests from anyone, which raises the risk of running untrusted code on your machine.
Do groups stop one job from reading another job's files?
No. Groups control repository access. Isolation between jobs comes from ephemeral runners or per job environments.
Sources
Last reviewed: 19 October 2026.