Skip to content

Choosing how to run self-hosted CI

One question decides most of this: must the compute be yours, and do several teams have to share it? If either half is no, something simpler than GitHub Actions Gateway (GAG) is probably right.

When not to choose GAG

If Use Not GAG, because
A vendor can run your jobs a managed runner service (Blacksmith, Namespace, Depot, WarpBuild, Ubicloud) they compete on build speed and price per minute; GAG does not (D.14)
Managed Kubernetes is cheap and your CI fits one cloud a cluster per team, in its own project a project boundary isolates harder than any shared cluster, and costs an API call
One team owns the cluster and the runners ARC GAG speaks the same protocol through the same client library. This is not a protocol argument (D.3)
Your compute is elastic cloud capacity, no cluster wanted RunsOn, terraform-aws-github-runner, Actuated a VM per job isolates without any namespace argument (D.11)

GAG is for what is left

The nodes are big and expensive, several teams have to share them, and that sharing has to be safe.

Location, location, location

Where each option can actually run is the fastest filter, and most comparisons omit it: "self-hosted" usually means "self-hosted on AWS".

Option Runs on
Managed runner services the vendor's infrastructure
RunsOn, terraform-aws-github-runner, ForgeMT AWS only
Actuated your hardware, vendor-hosted control plane
ARC, GAG any conformant Kubernetes cluster, including on-premises and air-gapped

On-premises, most of this list is already gone

Only the last row is left, ruled out by the same constraint that made you self-host in the first place, before a single feature is compared.

That also breaks account-per-tenant isolation, which is ForgeMT's model and the serious argument against a shared cluster: an AWS account is a free API call, but its bare-metal equivalent is a hardware purchase, and partitioned capacity cannot be shifted between tenants. Appendix D.9 takes it seriously and states both trade-offs.

Multi-tenant platforms are an order of magnitude more complex

Adding teams does not add a feature, it adds roles. Three end up in the room, each with different powers and a different blast radius, and holding them apart is most of the work. ARC models the first two as one person, which is coherent for a single-owner cluster and is why it has no primitive separating them.

The boundary How GAG draws it
Platform sets a cap the tenant cannot raise ResourceQuota is platform-owned; the controller has no write verb on it
Platform grants privilege, tenant cannot self-grant Pod Security Admission is a namespace label; privileged shapes come from a platform ClusterRunnerTemplate
Platform bounds priority, tenant composes within it priorityTiers draws from a platform allowlist, editable without a restart
Tenant self-serves without cluster-admin one ActionsGateway provisions controller, proxy, RBAC and policies inside the quota
Tenant sees its own data only metrics and a dashboard per tenant, plus a separate platform view
Contributor's code is contained default-deny egress, per-tenant egress identity, Kata workers

Each role and what it cannot do: Personas.

A paved road is worth more than a trail map

  • Feature comparisons tell you what can be done.
  • Reference architectures prove it has been validated, and tell you how to do it.
  • Runbooks, dashboards, and alerts tell you how to operate it.

Measured 2026-08-06 against ARC 0.14.2 and master.

GAG ARC
Sandboxed workers 500-line guide, default in GAG's own end-to-end CI, validated on named kernels, no privileged container same runtimeClassName field; no doc covers it, absent from chart values, two closed issues ever
Observability 20 alert rules, two dashboards for two audiences, per-tenant metrics, redaction before any log line one per-scale-set sample dashboard, no alert rules, metrics opt-in; dashboard request open since 2025-01-13

No offering is perfect, yet…

Here is where GAG loses, and to whom. No single alternative holds all of these, which is why the middle column matters as much as the first.

What GAG lacks Who has it Tracked
Install base ARC 6,417 stars, ForgeMT 211, GAG 3 (2026-08-06) see below
Commercial support ARC, and every managed service none planned, by design
Multi-label runs-on ARC, since 0.14.0 (2026-03-19) Q726
container:/services: without privilege ARC, via containerMode: kubernetes Q727
In-cluster cache GitLab Runner, managed services. Not ARC worker cache backend
GHES tested on a real appliance ARC flagged untested; needs an operator with one
Bound GitHub runner group ARC, via runnerGroup Q712, gating the next release
Default-tier latency metrics nobody: a defect, not a rival's feature Q713, gating the next release

Two footnotes on that table.

Cache is usually misread. actions/cache works on GAG today; what is missing is a cache inside the cluster. ARC has none either, so this is not a reason to prefer ARC.

Install base has no engineering answer. If being the first production deployment is unacceptable, that settles it. The only counterweight is the evidence trail: dated measurements, failure modes documented with the incidents that found them, and a published retraction of the project's own best number once it proved unmeasurable.

Reading further

Appendix D argues every alternative in full, including Prow, which is why GAG does not claim automatic re-run is novel, and GitLab Runner, which faces the identical problem and resolves it the other way. Why GAG? is the capability-by-capability comparison against ARC.