Use ephemeral runners wherever you can. An ephemeral runner takes one job on a clean environment and is then removed, so a compromised job cannot leave malware, cached tokens or poisoned files for the next one. Persistent runners are faster to reuse but carry state from job to job.
Definitions
Ephemeral runner. A runner created for a job and discarded after it. GitHub calls its version just-in-time, and says these self-hosted runners perform at most one job before being automatically removed.
Persistent runner. A long-lived machine or container that picks up many jobs over time.
GitHub's hardening guidance notes that self-hosted runners, unlike GitHub-hosted ones, are not guaranteed to run in ephemeral clean virtual machines. It also warns that reusing the same hardware for just-in-time runners can expose information from earlier jobs, so the automation must provide a clean environment each time.
Comparison
| Factor | Ephemeral | Persistent |
|---|---|---|
| Leftover files from the last job | None, if the environment is rebuilt | Possible |
| Credentials cached on disk | Gone with the runner | May remain |
| Startup time | Slower unless pre-built images are used | Fast |
| Cost model | Pay per job, scale to zero | Idle capacity |
| Effort to operate | Needs automation | Simple at first |
What persistent runners risk
A job that runs attacker-influenced code, such as a build triggered by a pull request, can write to anything the runner user can write to. On a persistent runner, that can be a modified tool, a changed cache or a planted script that a later, more privileged job executes. The later job might hold deployment credentials.
Decision guide
- Public repository? GitHub says self-hosted runners should almost never be used for public repositories, because anyone can open a pull request. Use hosted runners.
- Jobs touch production credentials? Use ephemeral runners, and keep those jobs on separate runners from untrusted builds.
- Build speed is the problem? Keep the runner ephemeral and pre-bake dependencies into its image rather than keeping a long-lived machine.
- Cannot automate teardown? Run persistent runners only for trusted, reviewed code, split by trust level, and wipe the workspace after every job.
Mixed fleets
Most teams end up with both. A sensible split is a pool of ephemeral runners for pull request builds and any job that touches secrets, and a small number of persistent runners for trusted, slow work such as large nightly builds from protected branches. Label them clearly, and make sure a workflow from an untrusted source can never select the persistent label. On GitHub, runner groups can control which repositories may use which runners. Review those assignments whenever a new repository is created.
Quick check
Is a container on a persistent host the same as ephemeral?
Not automatically. If the host keeps volumes, caches or a shared Docker daemon, state still carries over.
Do ephemeral runners remove the need for least privilege?
No. A clean runner still has the credentials you give it during the job.
Safeguard scans dependencies and SBOMs from whichever runner you use, and policies and gates can block a build on findings. The free scan needs no setup.
Sources
Last reviewed: 19 October 2026.