Self-Hosted vs Managed GitHub Actions Runners
Self-hosted runners look dramatically cheaper because the comparison usually counts only EC2 and ignores the idle time, the engineer, and the security boundary you just took ownership of.
The self-hosted case is easy to make on a spreadsheet: a c6i.large is a fraction of $0.006/min, so why pay a managed provider? The reason the spreadsheet misleads is that it compares a per-minute rate against an hourly instance that is billed whether or not a job is running.
A managed runner is billed only while a job executes. A self-hosted fleet is billed continuously and utilised intermittently, and CI load is extremely spiky: near-zero overnight, saturated at 11am. Sizing for the peak means paying for the trough.
Standard GitHub-hosted runners are free and unlimited on public repositories, and public repos get 4 vCPU and 16 GB rather than the 2 vCPU and 8 GB private repositories receive. No paid runner beats free, so everything below assumes a private repository past its included minutes.
The comparison people usually skip
| Self-hosted | Managed | |
|---|---|---|
| Billed for | Instance uptime, busy or idle | Job execution time only |
| Utilisation | Typically 10-30% on spiky CI load | Effectively 100% |
| Scale-out | Yours to build and operate | Included |
| Runner images | You build and maintain | Maintained for you |
| Security boundary | Your network and IAM | Provider's isolation |
| Fork PR risk | Untrusted code on your infrastructure | Isolated per job |
| On-call | You | Provider |
| Best at | High steady volume, special hardware, residency | Spiky load, small teams, no ops capacity |
Compute the real number, not the instance price
The honest self-hosted cost is instance cost divided by utilisation, plus the engineering time to operate it. Utilisation is the term that decides the comparison and the one most estimates omit.
# effective cost per BUSY minute
# = (instance $/hr / 60) / utilisation
#
# a $0.10/hr instance at 20% utilisation:
# ($0.10 / 60) / 0.20 = $0.0083 per busy minute
#
# which is MORE than GitHub-hosted list at $0.006/min,
# before counting a single hour of engineering time.The security trade is the part that is hard to reverse
- A self-hosted runner executing a fork pull request runs untrusted code on your infrastructure. GitHub documents this as not recommended for public repositories, and it is the single largest risk in this decision.
- Runners must be ephemeral. A reused runner leaks state between jobs, and a job that persists something malicious persists it for the next one.
- Anything the runner can reach on your network, a compromised job can reach.
Where self-hosted genuinely wins
- Steady, high, predictable volume where utilisation is actually high.
- Hardware nobody rents by the minute: GPUs, specific CPU features, very large memory, macOS on owned hardware.
- Data residency or private network access that a managed provider cannot offer.
- You already operate a fleet for other reasons, so the marginal operational cost really is near zero.
How to evaluate a managed runner honestly
Runner vendors compete on a headline per-minute rate, and the rate is rarely what decides the bill. Measure the whole job, on your own pipeline, before committing.
- Compare at equal machine shape. A cheaper per-minute rate on fewer vCPUs or less RAM is not cheaper per unit of work.
- Check billing granularity. Per-minute rounding costs real money on a wide matrix of short jobs; per-second does not.
- Include queue and boot time. A runner that is cheaper per minute but slower to start can cost more per merge.
- Count your re-runs. If a meaningful share of your runs are retries of a failed job, you are paying for the same work twice at whatever rate you negotiated, and no rate card prices that.
- Verify the free tier is recurring. A one-time credit is not a free tier.
The verdict
Spiky load, which is most teams: managed wins on true cost once you divide by utilisation, before counting operational time at all.
High steady volume with existing infrastructure expertise: self-hosted wins, and the gap widens with scale.
Special hardware or residency requirements: self-hosted or a BYOC provider, because managed cannot offer it at any price.
Public repositories: do not put self-hosted runners on them. Fork pull requests execute untrusted code on your infrastructure.