Build a Self-Hosted GitHub Actions Runner on Kubernetes (2026)
GitHub-hosted runners are slow and expensive at scale. Here's how to set up self-hosted GitHub Actions runners on Kubernetes with auto-scaling using Actions Runner Controller.
GitHub-hosted runners have 2 CPUs and 7GB RAM. They start cold. They can't access your private network. And at scale, they get expensive.
Self-hosted runners on Kubernetes solve all of this — and with Actions Runner Controller (ARC), they auto-scale based on your queue.
What You'll Build
- Actions Runner Controller (ARC) installed via Helm
- Runner scale set that auto-scales from 0 to N runners
- GitHub Actions workflow using your self-hosted runners
- Runners with access to your cluster's private network
Prerequisites
- Kubernetes cluster (EKS, GKE, or local k3s)
kubectlandhelminstalled- GitHub repository or organization
- GitHub Personal Access Token (PAT) with
reposcope
Step 1: Install cert-manager
ARC requires cert-manager for webhook TLS:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=trueWait for cert-manager pods to be ready:
kubectl get pods -n cert-managerStep 2: Install Actions Runner Controller
helm repo add actions-runner-controller \
https://actions-runner-controller.github.io/actions-runner-controller
helm repo update
# Create namespace
kubectl create namespace arc-systems
# Install ARC
helm install arc \
--namespace arc-systems \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controllerStep 3: Create GitHub PAT Secret
# Create a GitHub PAT with repo/admin:org scope
# Store it as a Kubernetes secret
kubectl create secret generic controller-manager \
--namespace arc-systems \
--from-literal=github_token=<YOUR_GITHUB_PAT>Step 4: Deploy a Runner Scale Set
A RunnerScaleSet auto-scales runners based on job queue depth:
# For a repository
helm install arc-runner-set \
--namespace arc-runners \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--set githubConfigUrl="https://github.com/YOUR_ORG/YOUR_REPO" \
--set githubConfigSecret=controller-manager \
--set minRunners=0 \
--set maxRunners=5Check runners registered:
kubectl get pods -n arc-runnersIn GitHub → Settings → Actions → Runners, you should see your runner set appear.
Step 5: Use Self-Hosted Runner in Workflow
# .github/workflows/build.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: arc-runner-set # name of your scale set
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t myapp:${{ github.sha }} .
- name: Run tests
run: |
docker run --rm myapp:${{ github.sha }} npm test
- name: Deploy to cluster
run: |
kubectl set image deployment/myapp \
myapp=myapp:${{ github.sha }} \
-n productionCustom Runner Image
The default runner image has common tools. For specific needs, build your own:
FROM ghcr.io/actions/actions-runner:latest
# Install additional tools
USER root
RUN apt-get update && apt-get install -y \
awscli \
kubectl \
helm \
&& rm -rf /var/lib/apt/lists/*
USER runnerConfigure ARC to use your custom image:
helm upgrade arc-runner-set \
--namespace arc-runners \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--set template.spec.containers[0].image=your-registry/custom-runner:latest \
--set githubConfigUrl="https://github.com/YOUR_ORG/YOUR_REPO" \
--set githubConfigSecret=controller-managerWhy Self-Hosted Runners Win at Scale
| GitHub-Hosted | Self-Hosted on K8s | |
|---|---|---|
| CPU/RAM | 2 vCPU, 7GB | You choose (4, 8, 16 vCPU) |
| Cold start | 30–60s | 10–15s |
| Cost | $0.008/min | Your cluster cost |
| Private network | No | Yes |
| Custom tools | Re-install each run | Baked into image |
| Auto-scaling | Fixed concurrency | 0 to N based on queue |
At 1000+ runs/month, self-hosted runners on Kubernetes are 3–5x cheaper than GitHub-hosted.
Troubleshooting
Runners not appearing in GitHub:
kubectl logs -n arc-systems \
deployment/arc-gha-runner-scale-set-controllerJobs not picked up:
kubectl get runners -n arc-runners
kubectl describe runner <runner-pod> -n arc-runnersPermission issues: Make sure your PAT has the correct scope:
- Repository:
reposcope - Organization:
admin:orgscope
What You've Built
A Kubernetes-native CI/CD runner fleet that:
- Scales to zero (no idle cost)
- Scales up automatically when jobs are queued
- Has direct access to your cluster's internal services
- Runs on your hardware with your custom tools
This is the setup used by teams running hundreds of pipelines per day.
Resources
- Actions Runner Controller GitHub — official ARC repo
- GitHub Actions Cheatsheet — workflow syntax reference
- Build CI/CD Pipeline with GitHub Actions + ArgoCD — full project walkthrough
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Build a Complete CI/CD Pipeline with GitHub Actions + ArgoCD + EKS (2026)
A full project walkthrough — from a simple app to a production-grade GitOps pipeline with automated builds, image scanning, and deployments to AWS EKS using ArgoCD.
Build a DevSecOps Pipeline with Trivy, SonarQube, and OPA from Scratch (2026)
Step-by-step project walkthrough: add security scanning, code quality gates, and policy enforcement to a GitHub Actions pipeline. Real configs, production-ready.
5 DevOps Portfolio Projects That Actually Get You Hired in 2026
Not just another list of project ideas. These are the specific projects that hiring managers at top companies are looking for — with exactly what to build and how to present them.