GitHub-hosted and self-hosted runners execute the same GitHub Actions workflows, but they assign infrastructure responsibility differently. GitHub-hosted runners are the default for teams that want clean managed execution, automatic capacity, and minimal runner operations. Self-hosted runners fit workloads that require custom hardware, controlled network access, specialized images, or sustained compute—provided the team can isolate, patch, monitor, and replace them safely.
The decision is not simply usage-based billing versus a monthly VM. It is managed job execution versus ownership of the machines that receive workflow code, credentials, artifacts, and network access.
GitHub-hosted vs self-hosted runners at a glance
| Decision area | GitHub-hosted runners | Self-hosted runners |
|---|---|---|
| Infrastructure ownership | GitHub manages the runner environment | Your team manages the host and runner lifecycle |
| Job environment | Newly provisioned environment for standard hosted jobs, with documented exceptions | Persistent unless your automation creates ephemeral runners |
| Scaling | Managed automatically within plan and concurrency limits | Must be designed and operated by your team |
| Customization | Standard images plus larger-runner options | Full control over OS, tools, hardware, storage, and network |
| Private access | Supported through selected hosted-runner networking patterns and larger-runner capabilities | Direct placement inside your controlled network |
| Security burden | Lower host-management burden | You own isolation, patching, cleanup, secrets exposure, and incident response |
| Cost shape | Usage and plan dependent; little idle infrastructure ownership | Infrastructure, storage, operations, and idle-capacity costs |
| Best default | Standard, bursty, or untrusted workflows | Specialized, private, or steady workloads with mature operations |
For most teams, begin with GitHub-hosted runners. Move a workload to self-hosted only when a documented constraint justifies the additional trust and operations boundary.
What a runner actually controls
A GitHub Actions runner is the system that downloads a job, prepares its workspace, executes workflow steps, accesses approved secrets and tokens, uploads logs or artifacts, and reports the result to GitHub.
That makes the runner a sensitive execution boundary. Workflow code may be able to:
- read files left by previous jobs
- access credentials exposed to the job
- reach internal services available from the runner network
- modify local tools or caches
- interact with container or virtualization sockets
- persist changes when the runner is reused
The hosting decision therefore affects security architecture, not only build performance.
GitHub-hosted runners: the managed default
GitHub-hosted runners are managed execution environments with supported operating-system images and preinstalled tooling. Standard jobs are provisioned for workflow execution and removed afterward according to the runner type.
This model is valuable when the team wants:
- low runner-maintenance overhead
- automatic capacity for bursty workloads
- standard Linux, Windows, or macOS environments
- simpler onboarding and troubleshooting
- reduced risk of state leaking between ordinary jobs
- no always-on build VM to patch or monitor
GitHub-hosted runners are especially appropriate for public repositories, fork-based pull requests, community contributions, and workflows that process code the infrastructure team does not fully trust.
Hosted does not mean unlimited or universally suitable. Workflows can encounter concurrency, timeout, disk, architecture, networking, or installed-tool constraints. GitHub also offers larger runners for eligible organizations that need more CPU, memory, disk, GPU options, runner groups, autoscaling, static IP options, or supported private-network features.
Before self-hosting, confirm that a standard or larger GitHub-hosted runner cannot satisfy the requirement with less operational risk.
Self-hosted runners: control with ownership
A self-hosted runner is a physical machine, VM, containerized environment, or cloud instance your team provides and manages. GitHub supplies the runner application, but your team owns the surrounding system.
Self-hosting provides control over:
- operating system and package versions
- CPU, memory, storage, and architecture
- custom compilers, SDKs, drivers, and build tools
- network location and private service access
- local caches and mirrors
- attached devices or specialized hardware
- runner images and lifecycle automation
It also transfers responsibility for:
- operating-system patching
- runner software updates
- host hardening
- access control
- capacity and queue management
- workspace cleanup
- log retention
- backup and recovery where required
- compromise detection and replacement
GitHub does not charge GitHub Actions minutes for self-hosted execution, but the runner is not free. Infrastructure, idle capacity, storage, automation, security controls, and engineering time are part of the cost.
Security: treat workflow code as potentially hostile
The most important self-hosted runner question is: Who can cause code to run on this machine?
GitHub recommends using self-hosted runners with private repositories because a public repository pull request can potentially execute dangerous code on the runner. The same trust concern can exist inside private organizations when repositories, contributors, reusable workflows, or third-party actions are broadly accessible.
A compromised self-hosted runner may expose:
- repository and workflow tokens
- deployment credentials
- package-registry credentials
- cloud or infrastructure access
- internal network services
- artifacts and caches
- credentials left by earlier jobs
- the runner host itself
Do not place a runner inside a trusted production network merely because a deployment job needs one internal endpoint. Reduce network reach, use short-lived credentials, separate build and deployment responsibilities, and restrict which repositories and workflows may target each runner group.
Persistent vs ephemeral self-hosted runners
A persistent runner remains registered and can process multiple jobs. It is simple to deploy but accumulates state and creates a larger persistence opportunity after compromise.
An ephemeral runner is registered for one job and automatically de-registers after that job. Automation then destroys or resets the environment before another job receives it.
GitHub recommends ephemeral self-hosted runners for autoscaling and does not recommend autoscaling persistent runners. Ephemeral execution helps reduce:
- cross-job workspace leakage
- stale credentials and files
- hidden tool or dependency drift
- compromised runners receiving future jobs
- inconsistent cleanup behavior
Ephemeral does not remove the need for security. The job can still access every credential and network resource made available during its lifetime. External log forwarding is also important because the runner may disappear immediately after failure.
For production self-hosting, prefer replaceable runner images and one-job lifecycle automation over manually maintained long-lived hosts.
Autoscaling options
Self-hosted autoscaling must respond to queued jobs, provision capacity, register runners, collect logs, remove completed environments, and handle failed or abandoned instances.
GitHub documents two main scale-set paths:
- Actions Runner Controller (ARC): GitHub’s recommended Kubernetes-based solution for teams that already operate Kubernetes and have the expertise to manage it.
- Runner Scale Set Client: A building block for platform teams creating custom autoscaling outside Kubernetes across VMs, containers, on-premises systems, and cloud environments.
ARC is not a reason to adopt Kubernetes only for CI. A team without an existing Kubernetes operating model may create more complexity than it removes. For modest workloads, VM-based ephemeral automation or GitHub-hosted larger runners can be the better choice.
Private network access
Private network access is a legitimate reason to evaluate self-hosting, but it is no longer an automatic decision.
GitHub-hosted runner options can support private connectivity through documented patterns such as:
- OIDC with an API gateway
- WireGuard-based overlays
- Azure private networking for supported larger-runner configurations
- static IP capabilities on eligible larger runners
Self-hosted runners remain useful when workflows require frequent, low-latency, or broad access to private build systems, package registries, testing environments, deployment targets, or licensed internal software.
Use the narrowest access model possible. A runner that builds code usually does not need unrestricted production database access. A deployment runner may not need access to development secrets. Separate runner groups and network segments by trust level and job purpose.
Cost: compare complete operating models
A useful cost comparison includes more than hosted-runner minutes and VM price.
GitHub-hosted cost includes
- eligible included usage from the GitHub plan
- additional usage where applicable
- larger-runner charges where selected
- artifact, cache, or storage considerations
- little direct host-management labor
Self-hosted cost includes
- compute and persistent storage
- idle or standby capacity
- image creation and patching
- autoscaling development
- logging and monitoring
- security hardening and incident response
- cache maintenance
- failed-job and queue troubleshooting
- engineering ownership
Self-hosting tends to become more attractive when workloads are steady, Linux-heavy, cache-sensitive, specialized, or expensive on hosted runner sizes. GitHub-hosted tends to remain stronger for bursty workloads, mixed operating systems, public contribution workflows, and teams without platform-engineering capacity.
Use current GitHub billing documentation and the live Raff pricing page for calculations. Do not reuse static prices from old articles because both products and usage patterns change.
Performance and caching
Self-hosted runners can improve job time when they are close to private services, use warm dependency caches, retain container layers, or have hardware sized specifically for the build.
Persistent caches also create risks:
- non-reproducible builds
- unbounded disk growth
- dependency poisoning
- state leaking between repositories
- hidden reliance on files not declared by the workflow
GitHub-hosted environments encourage cleaner reproducibility but may spend more time downloading dependencies or rebuilding caches.
Measure:
- queue time
- provisioning time
- dependency download time
- build and test duration
- artifact upload time
- failure and retry rate
- monthly utilization
- operator time
A faster individual job does not automatically produce a lower-cost or safer CI system.
When GitHub-hosted runners are the right choice
Prefer GitHub-hosted runners when:
- workflows are standard and bursty
- the repository accepts public or fork-based contributions
- the team wants minimal runner maintenance
- standard or larger hosted images meet the requirements
- multiple operating systems are needed
- clean job environments are more valuable than persistent caches
- private access can be solved with a narrower hosted networking pattern
- the team does not have capacity to operate runner infrastructure securely
This is the correct default for most small teams.
When self-hosted runners are the right choice
Prefer self-hosted runners when:
- custom hardware or software is mandatory
- the workload requires controlled internal network placement
- sustained build volume can justify owned compute
- local caches or package mirrors materially improve throughput
- compliance requires an environment your team controls
- the team can create replaceable runner images
- runner access can be restricted by repository, workflow, network, and credential scope
- monitoring, patching, logging, cleanup, and incident response have named owners
A self-hosted runner is ready only when its lifecycle is designed, not merely when the runner application connects successfully.
Decision framework
Use this order:
- Trust: Can untrusted contributors, forks, or third-party workflows cause code to run?
- Capability: Do standard or larger GitHub-hosted runners meet the hardware and software requirement?
- Network: Can private access be provided without placing the runner broadly inside production?
- Utilization: Is job volume steady enough to justify owned capacity?
- Lifecycle: Can runners be provisioned, patched, logged, and destroyed automatically?
- Isolation: Are repositories and job types separated by runner group and network policy?
- Credentials: Can workflows use short-lived, narrowly scoped authentication?
- Operations: Who owns queues, failures, runner updates, capacity, and compromise response?
If the first four questions do not clearly support self-hosting, stay with GitHub-hosted runners.
:::cluster
How this applies on Raff
A Raff Linux VM can host a persistent runner, an image-builder, or the control layer for ephemeral VM-based runners. Choose resources from measured build CPU, memory, disk, concurrency, and cache requirements rather than using a generic CI size.
Use Private Cloud Networks to separate runners from public interfaces and reach only approved internal services. Apply Security controls around administrative access, firewall policy, credentials, and monitoring.
For light or irregular CI, GitHub-hosted runners may remain the lower-cost operational choice. Raff becomes relevant when runner ownership solves a concrete requirement: private placement, custom images, controlled hardware, predictable sustained capacity, or integration with infrastructure the team already operates.
Conclusion
GitHub-hosted runners optimize for managed execution and low operational burden. Self-hosted runners optimize for environmental control and network placement while transferring the runner’s security and lifecycle to your team.
Start hosted. Move selected workloads to self-hosted when the requirement is measurable, and prefer ephemeral, replaceable runners for production autoscaling. Do not turn a permanent build VM into a trusted bridge between arbitrary workflow code and the rest of your infrastructure.
