Most small teams should use hosted runners and move to self-hosted only for a clear need such as private network access or special hardware. Self-hosted runners are not guaranteed to be clean between jobs, and anyone who can trigger a workflow can run code on them, so they need real hardening.
What changes when you host the runner
A hosted runner is a fresh virtual machine for each job. GitHub's documentation describes hosted runners as ephemeral and isolated, so there is no way to compromise the environment persistently. You only get what the job brought in.
A self-hosted runner is a machine you deploy and update yourself. GitHub's docs say these runners do not have guarantees about running in clean virtual machines and can be persistently compromised by untrusted code in a workflow. GitLab's runner documentation says the same about non-ephemeral runners: a job from one repository can affect other repositories the same runner serves.
Good reasons to self-host
- The build must reach a private network, database or internal registry that hosted runners cannot reach.
- You need hardware or an operating system that is not offered, or jobs far larger than hosted limits allow.
- You already run spare compute and want to use it.
"It is cheaper" is a weaker reason once you count the time to patch and monitor the machines. GitHub's docs are plain that you are responsible for updating the operating system and all other software on them.
Risks to weigh
The questions that matter are about who can start a job, and what the machine can see.
- Who can open a pull request? GitHub's guidance says self-hosted runners should almost never be used for public repositories. For private ones, anyone with read access who can fork and open a pull request may be able to run code on the runner, including reaching secrets and the token.
- What lives on the host? SSH keys, cloud credentials and metadata services all count. The docs advise keeping sensitive information on these machines to a minimum.
- How wide is the runner's reach? An organization level runner can take jobs from many repositories, so one compromise has a wide effect. Separate runner groups limit that.
- Do jobs overlap? GitHub notes that secrets passed as command line arguments can be seen by another job on the same runner, which is one reason "destroy it after each job" is not a full answer.
A decision guide
| Your situation | Sensible default |
|---|---|
| Public repository | Hosted runners |
| Private repository, no private network needs | Hosted runners |
| Private network needed, trusted contributors only | Self-hosted, ephemeral, separate group |
| Many repositories, mixed trust | Separate runner groups per trust level |
If you do self-host
Use just-in-time runners where you can. GitHub's REST API can create runners that perform at most one job and are then removed. Docs add a caution: reusing the same hardware can leak information, so rebuild a clean environment each time with automation.
Keep the runner account unprivileged. GitLab's docs warn that the shell executor runs jobs with the runner user's permissions and can steal code from other projects on the host, and that Docker privileged mode gives a job root capabilities on the host. Avoid both for anything you do not fully trust. See our post on privileged mode on CI runners.
Finally, treat fork pull requests as untrusted. Require approval before workflows from outside contributors run. GitHub's own warning is that approval policies were designed for compute abuse and that on self-hosted runners, malicious workflow code runs once a pull request is approved.
Where Safeguard helps
Safeguard scans dependencies and can gate a pull request on policy before it merges, whichever runner executes the build. It does not replace runner isolation.
Quick check
Is a self-hosted runner in a container safe enough?
A container helps only if it is destroyed after each job and runs without privileged mode. A long-lived container that serves many jobs keeps the same carry-over risk.
Can environment approvals protect a self-hosted runner?
They protect environment secrets until a reviewer approves, but GitHub's docs say those jobs are not isolated on self-hosted runners and share the same risks.
Sources
- Secure use reference, GitHub Docs
- Self-hosted runners, GitHub Docs
- Security for self-managed runners, GitLab Docs
- Managing GitHub Actions settings for a repository, GitHub Docs
Last reviewed: 19 October 2026.