Safeguard
Security Guides•

Ephemeral or persistent CI runners: which should you use?

Ephemeral runners start clean and run one job, so a compromised job leaves nothing behind. Persistent runners are faster but carry state between jobs.

Safeguard Security Team
3 min read

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

FactorEphemeralPersistent
Leftover files from the last jobNone, if the environment is rebuiltPossible
Credentials cached on diskGone with the runnerMay remain
Startup timeSlower unless pre-built images are usedFast
Cost modelPay per job, scale to zeroIdle capacity
Effort to operateNeeds automationSimple 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

  1. 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.
  2. Jobs touch production credentials? Use ephemeral runners, and keep those jobs on separate runners from untrusted builds.
  3. Build speed is the problem? Keep the runner ephemeral and pre-bake dependencies into its image rather than keeping a long-lived machine.
  4. 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.