How to Secure a Self-Hosted Runner Against Public-Repo PRs in GitHub Actions
GitHub explicitly warns: do not use self-hosted runners on public repositories, because a fork PR can run arbitrary code on your machine.
Self-hosted runners are not sandboxed by default, so a malicious fork PR could execute on your host. Keep them on private repos, require approval for first-time contributors, and never use persistent runners for untrusted code.
Steps
- Avoid self-hosted runners entirely on public repositories.
- Set Actions to require approval for all outside collaborators.
- Use ephemeral runners so one PR cannot poison the next job.
- Restrict runners to selected repos with a runner group.
Require approval (org policy)
GitHub settings
# Settings to Actions to General to Fork pull request workflows
# from outside collaborators:
# "Require approval for all external contributors"
# Also: do not enable self-hosted runners on the public repo.Gotchas
- A persistent runner keeps state between jobs; a compromised job can leave a backdoor for the next one. Ephemeral runners mitigate this.
- GITHUB_TOKEN is read-only on
pull_requestfrom forks, but the runner host itself is still exposed if a fork job runs on it. - Managed runners (Latchkey) run each job on a fresh, isolated machine, which removes the persistent-host attack surface for private-repo CI.
Related guides
How to Set Up Runner Groups in GitHub ActionsOrganize self-hosted GitHub Actions runners into groups to control which repositories and workflows can use t…
How to Run an Ephemeral Self-Hosted Runner in GitHub ActionsConfigure a self-hosted GitHub Actions runner with the --ephemeral flag so it runs exactly one job, then dere…