Git, CI/CD & GitOpsC · Runner CapacityCost
Cost of hosted versus self-hosted runners — the financial trade-off
What you'll learn
- Decompose the cost of hosted runners into per-minute rates and included minutes
- Decompose the cost of self-hosted runners into compute, operations, and risk premiums
- Compute a break-even point between hosted and self-hosted for a given workload
- Recognise when the cost comparison is dominated by factors beyond compute
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
The choice between hosted and self-hosted runners is often presented as a simple price comparison. It is not. Each option has cost components the other does not, and the break-even depends on the team’s operational capacity as much as it does on the per-minute rate.
Cost components of hosted runners
GitHub-hosted runners are priced by per-minute rate, with monthly included minutes per plan:
flowchart LR
H["Hosted runner cost"] --> R["Per-minute rate\n(by runner class)"]
H --> S["Storage overage\n(GB-month)"]
H --> O["Operational overhead\nzero"]
H --> T["Time overhead\ncold-start, image pull,\nephemeral cleanup"]
- Per-minute rate. Published in GitHub’s billing documentation. Different for Linux, Windows, macOS, and for larger-runner classes. The visible number.
- Storage overage. Beyond the included GB-month, storage costs extra. Often invisible until the bill arrives.
- Operational overhead. Zero. GitHub patches the host, rotates the image, and reclaims the VM. This is the single largest line item, even though it appears as zero on the invoice.
- Time overhead. Cold-start latency on the hosted
runner is small but non-zero, and the runner is ephemeral
- state does not survive across jobs unless cached.
Cost components of self-hosted runners
Self-hosted runners invert every line item:
flowchart LR
S["Self-hosted cost"] --> C["Compute\nVM or pod"]
S --> OPS["Operations\npatching, image,\nregistration, secrets"]
S --> RISK["Risk premium\ncompromise, persistence,\ncompliance"]
S --> TIME["Time overhead\nvariable cold-start"]
- Compute. The VM, the bare-metal host, or the Kubernetes pod. Billed by the cloud provider or amortised across on-premises hardware.
- Operations. Patching the host, rebuilding the runner image, registering and deregistering runners, rotating the registration token, managing secrets on the runner. This is real engineering time, often the largest hidden cost.
- Risk premium. A self-hosted runner is an actor in the CI/CD threat model. A compromise is a production compromise; persistence between jobs is a feature of the platform. Compliance overhead and incident response cost should be priced in.
- Time overhead. Variable: a warm self-hosted runner has near-zero cold start; a scale-to-zero ARC pool has a Kubernetes pod-startup cost that hosted runners do not have.
Computing break-even
The break-even is the workload size at which the operational overhead of self-hosted is paid back by the compute savings. A simplified model:
HOSTED_RATE=0.008
SELF_RATE=0.002
OPS_OVERHEAD_USD_MONTH=2000
JOB_MIN_PER_MONTH=100000
HOSTED_COST=$(awk -v r="$HOSTED_RATE" -v m="$JOB_MIN_PER_MONTH" \
'BEGIN { printf "%.0f", r*m }')
SELF_COST=$(awk -v r="$SELF_RATE" -v m="$JOB_MIN_PER_MONTH" \
'BEGIN { printf "%.0f", r*m }')
echo "Hosted: $HOSTED_COST; Self-hosted compute: $SELF_COST"
echo "Self-hosted total: $((SELF_COST + OPS_OVERHEAD_USD_MONTH))"
For 100,000 job-minutes/month at these rates, hosted costs $800 and self-hosted compute costs $200 - but the operational overhead is $2,000/month, so self-hosted loses on a small workload and wins on a large one.
When the comparison is dominated by other factors
The cost comparison is dominated by compute in only one case: a large, stable workload with no special requirements. Most teams have at least one of these complicating factors:
- Specialised hardware. GPUs, large-memory, ARM, macOS. Hosted runners offer these as larger runners at a premium; self-hosted requires buying or renting the hardware. The comparison shifts towards hosted.
- Network proximity. A runner that needs to talk to a private VPC over a VPN is much faster on self-hosted. The speedup compounds across the day’s jobs.
- Compliance. A runner that must not have access to public network, or that must run on FIPS-validated hosts, may be impossible on the hosted platform.
- Burst. A workload that spikes to 100 concurrent jobs once a quarter is cheaper on hosted - the burst does not have to be provisioned for year-round.
Production discipline
- Price the operations, not just the compute. Compute is visible; operations are not.
- Re-derive the break-even annually. Cloud prices move; operational cost changes as the team grows.
- Hybrid is the realistic answer. Production-sensitive workloads on self-hosted with strict IAM; sporadic or experimental workloads on hosted.
- Don’t optimise for the bill; optimise for the total cost. Risk premiums and incident response are part of the total.
- Document the decision. The hosted-vs-self decision changes; the reasoning should be in the capacity plan.
Cross-course references
- Git, CI/CD & GitOps - Parts XL (Runners) and LXVI (Supply) cover the security framing of the decision.
- FinOps for Production Sysadmins - Part IV (BuildVsBuy) covers the same trade-off for general workloads.
- Kubernetes for Production Sysadmins - Part XXIX (Cost) covers cluster cost components in depth.
Quiz
Knowledge check · 4 questions
Q1. Which cost component is most often missing from a naive hosted-vs-self-hosted comparison?
Q2. Self-hosted runners are not always cheaper than hosted runners once a team runs more than 1,000 jobs per month.
Q3. Name the three non-compute cost components of self-hosted runners that a hosted-runner comparison must include.
Q4. Recommend a hosted-vs-self-hosted split for a team running production CI on a private VPC with periodic burst to 80 concurrent jobs.
Team T runs 80,000 job-minutes/month on average. They have a private VPC that all jobs must reach. Once a quarter they have a release-week burst of 200,000 job-minutes in 48 hours. They have a small platform team with no Kubernetes experience but strong AWS expertise.
Passing score: 75%. Answers are checked in this browser.