Deploying GitHub Self-Hosted Runners on Your Home Kubernetes Cluster with ARC
If you followed my last post, Building a Home Lab Kubernetes Cluster with Old Hardware and k3s, you now have a proper x86 Kubernetes cluster humming away on your old laptops. So, what’s next? Time to put that cluster to work—let’s run GitHub Actions jobs on your own hardware!
Why? Because self-hosting your runners is faster, gives you full control (no GitHub minutes limit!), and lets you run bigger jobs (CI, builds, ML, you name it) on your home infra. And with Actions Runner Controller (ARC), managing runners at scale on Kubernetes is surprisingly easy.
Here’s how to set it all up.
What is ARC and Why Should You Care?
ARC (Actions Runner Controller) is an open-source Kubernetes operator from GitHub. It spins up and manages GitHub Actions runners as Kubernetes pods—no more manually registering runners, no more pets, just cattle. Runners auto-scale up and down as jobs arrive. It’s perfect for CI/CD, especially on clusters you own.
Here’s a high level view of how it works under the hood
Prerequisites
- Working Kubernetes cluster (see previous post)
kubectl
andhelm
installed on your machine- A GitHub Personal Access Token (PAT) with
repo
andadmin:org
scopes
1️⃣ Pre-Setup: Quick Checks
Make sure you have what you need:
which helm
kubectl version --client
helm list -A
If those commands work, you’re good to go.
2️⃣ Install ARC Controller
Let’s install the ARC controller into your control plane namespace:
NAMESPACE="actions-runner-controller"
helm install arc \
--namespace "${NAMESPACE}" \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
This deploys the controller which manages your runners.
3️⃣ Deploy a Runner Scale Set
Time to create the runners that will actually do the work. Replace the example GitHub URL and PAT with your details:
INSTALLATION_NAME="arc-runner-set"
NAMESPACE="arc-runners"
GITHUB_CONFIG_URL="https://github.com/youruser/yourrepo"
GITHUB_PAT="ghp_123456..."
helm install "${INSTALLATION_NAME}" \
--namespace "${NAMESPACE}" \
--create-namespace \
--set githubConfigUrl="${GITHUB_CONFIG_URL}" \
--set githubConfigSecret.github_token="${GITHUB_PAT}" \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set
GITHUB_CONFIG_URL
: The repo or org you want to run jobs for.GITHUB_PAT
: Your Personal Access Token.
4️⃣ Check That It’s Working
Verify the controller and runner pods are up:
# Controller
kubectl get pods -n actions-runner-controller
# Runners
kubectl get pods -n arc-runners
# Runner set status
kubectl get AutoscalingRunnerSet -A
kubectl describe AutoscalingRunnerSet arc-runner-set -n arc-runners
You should see your runners show up as pods. If you trigger a workflow in your GitHub repo, you’ll see a pod spin up, do the job, and then shut down—magic.
5️⃣ Testing It Out
Here’s the fun part. Create a simple GitHub Actions workflow in your repo to test the runners:
name: Test ARC Runners
on: [push]
jobs:
build:
runs-on: arc-runners # This tells GitHub to use your self-hosted runners. Use the NAMESPACE name you defined in step 3.
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run a script
run: echo "Hello from ARC Runner!"
- name: List files
run: ls -la
Here’s ci.yaml from my githubstats repo if you need a working example: ci.yaml
Make a commit to trigger the workflow. You should see the runner pod spin up, execute the job, and then terminate.
5️⃣ Monitoring (Bonus: Grafana)
Want to geek out and monitor your runners? If you’ve set up Prometheus/Grafana (see my upcoming post if not!), you can:
- Check pod CPU/memory usage
- Track how many runners are running
- See logs for each pod
Handy queries for Grafana dashboards:
# Number of ARC runner pods
kube_pod_status_phase{namespace="arc-runners", phase="Running"}
# Pod CPU usage
rate(container_cpu_usage_seconds_total{namespace="arc-runners"}[5m])
6️⃣ Useful Commands for Day-to-Day Ops
# Watch runner scaling in real-time
kubectl get AutoscalingRunnerSet -n arc-runners -w
# See events and troubleshoot
kubectl get events -n arc-runners --sort-by=.metadata.creationTimestamp
# Pod logs (for a specific runner)
kubectl logs -n arc-runners <pod-name>
Troubleshooting Tips
- Runner not connecting? Double-check your PAT and network access.
- Pods stuck or crash-looping? Check logs for clues and make sure your cluster has enough resources.
- Can’t see runners in GitHub? Make sure the config URL matches your repo/org and the PAT has correct scopes.
That’s It! You’re Running GitHub Actions on Your Own Cluster
You now have GitHub Actions jobs running at home on your cluster, scaling up and down automatically. No more slow or limited runners. Your home lab just levelled up—CI/CD, builds, ML, you name it.
Stay tuned for my next post where I’ll show you how to get beautiful observability dashboards and set up alerting for your home cluster.
Happy automating!
Questions? Want to show off your setup? Ping me on X (Twitter) or drop a comment below!