Why GitHub moved its own CI to Actions
GitHub’s Developer Experience (DX) team has spent the last several years iterating on CI systems for building and testing GitHub.com. The goal has always been the same: give engineers a fast, reliable path from commit to deploy. But maintaining in-house CI infrastructure presented two recurring problems — scaling the system to handle demand, and providing stable, ephemeral build environments.
The release of Actions larger runners changed the calculation. These managed virtual machines offer more CPU, RAM, and disk than standard GitHub-hosted runners, plus features like autoscaling that GitHub handles entirely. That let the DX team stop maintaining CI infrastructure and instead focus on the developer experience layer on top of it.
Running 15,000 jobs an hour at peak
Larger runners scale to match job demand automatically. GitHub creates multiple instances of a runner that scale up and down as engineers push work through the system. At peak utilization, that translates to:
- 4,500 concurrent 32-core runners
- 125,000 build minutes per hour
- Roughly 15,000 jobs queued and run within an hour
- About 150,000 cores of compute allocated
All of that scaling complexity is handled by GitHub itself — the DX team no longer worries about provisioning or capacity planning.
Custom VM images speed up builds dramatically
Prebuilt runner images work fine for many of GitHub’s thousands of internal projects. But complex production services often need specialized tooling that isn’t in the standard image. Installing that during a CI job wastes time and slows feedback loops.
To solve that, GitHub has been building support for launching larger runners from custom VM images. The feature is still in beta, but GitHub already uses it internally in two ways:
- Bundling required software. Anything unique to GitHub or a specific project gets pre-installed on the image before a workflow ever starts.
- Bootstrapping cache. The custom image can include a pre-built version of a project’s source code, so workflows start from a cached state rather than building from scratch.
Because GitHub.com changes fast, that cached source becomes stale quickly, which would slow workflows down again. The DX team worked with the Actions engineering team to build an API that updates custom images multiple times a day, keeping the cached source fresh.
The impact on build times has been dramatic. Without custom images, some workflows took around 50 minutes from start to finish. Today they take 12 minutes.
Reusable workflows keep CI configurations central
GitHub has thousands of repositories — from production services to small internal tools. A core goal for the DX team is paving a path so any of them can run CI without duplicating configuration across repos. Reusable workflows make that practical.
With reusable workflows, a single repository centrally manages prebuilt workflows that other repositories in an organization can consume. GitHub created several such workflows in one central repository, and consuming repositories reference them with just a few lines of YAML.
on:
workflow_call:
inputs:
cibuild-script:
description: 'Which cibuild script to run.'
type: string
required: false
default: "script/cibuild"
secrets:
service-api-key:
required: true
jobs:
reusable_workflow_job:
runs-on: gh-larger-runner-medium
name: Simple Workflow Job
timeout-minutes: 20
steps:
- name: Checkout Project
uses: actions/checkout@v3
- name: Run cibuild script
run: |
bash ${{ inputs.cibuild-script }}
shell: bash
name: my-new-project
on:
workflow_dispatch:
push:
jobs:
call-reusable-workflow:
uses: github/internal-actions/.github/workflows/default.yml@main
with:
cibuild-script: "script/cibuild-my-tests"
secrets:
service-api-key: ${{ secrets.SERVICE_API_KEY }}
Because the runner can be defined inside the reusable workflow itself, GitHub guarantees that everyone using a given workflow runs on the designated larger runner pool. Projects don’t need to know or care which runner type to request.
Reusing workflow outcomes when nothing changed
For some repositories, consecutive workflow runs execute against identical file contents — the Git tree IDs match because nothing changed between commits. In those cases, running the full CI suite is wasted compute and wasted engineer time.
The DX team worked with engineering to build a feature that lets workflows reuse the outcome of a previous run when the outcomes would be identical. CI checks are bypassed, and engineers don’t wait for work that’s already been validated.
Internally, this saves anywhere from 300 to 500 workflow runs per day.
Secure access to private services
Some internal workflows need to reach GitHub private services — artifact storage, application metadata services, test harness invocations — that live inside GitHub’s virtual private cloud (VPC). Larger runners are isolated from production environments, so they can’t reach those services directly through normal network paths.
GitHub’s solution is a remote access gateway based on OIDC tokens. The flow works like this: a workflow running on a larger runner mints an OIDC token, passes it to a remote access gateway, and the gateway validates the token before proxying the request to the private service inside the VPC.

This gives GitHub engineers secure remote access from larger runners to private resources without exposing those services broadly. GitHub has open sourced the basic scaffolding of this gateway in the github/actions-oidc-gateway-example repository.
GitHub runs on GitHub
The transition to Actions for GitHub.com CI came with real engineering tradeoffs and product feedback loops. The DX team got a managed, autoscaling CI platform. The Actions team got a demanding internal customer using the product at extreme scale — 150,000 cores at peak — and driving features like custom images and workflow outcome reuse.
The result is a faster, lower-maintenance CI system for GitHub’s engineers, and a better product for external customers who benefit from the same features.



