I recently set up a self-hosted GitHub Action Runner on Kubernetes using the Action Runner Controller, aiming for more control over my CI/CD pipeline. This involved configuring Kubernetes with DNS, ingress, and microk8s, and securing my private Docker registry with TLS and a custom Certificate Authority.

Create GitHub App and Secrets#

On your repository, go to Developer settings and create and install a GitHub App. You will obtain the necessary details for creating a Kubernetes secret:

kubectl create secret generic controller-manager -n actions
--from-literal=github_app_id=YOUR_GITHUB_APP_ID
--from-literal=github_app_installation_id=YOUR_INSTALLATION_ID
--from-literal=github_app_private_key=YOUR_PRIVATE_KEY

Helm Chart Values#

Below is an example of the values you might use for the Helm chart to deploy the Action Runner Controller:

githubWebhookServer:
  enabled: true
  replicaCount: 1
  secret:
    enabled: true
    create: true
    name: "github-webhook-server"
    github_webhook_secret_token: "GITHUB_WEBHOOK_TOKEN"
  ingress:
    enabled: true
    ingressClassName: ""
    annotations:
      kubernetes.io/ingress.class: traefik-ingress-class
      kubernetes.io/tls-acme: "true"
    hosts:
      - host: github-runner.yourdomain.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: github-runner-cert
        hosts:
          - github-runner.yourdomain.com

When setting up your GitHub Action Runner on Kubernetes, focus on two crucial steps:

  • Update the ingress section in your YAML with your actual domain name, ensuring GitHub can reach your webhook server.
  • Use tls-acme: "true" to integrate cert-manager.

GitHub Webhook#

Set the Payload URL in your repository’s webhook settings to github-runner.yourdomain.com, which should point to your Kubernetes-hosted GitHub Action Runner’s address. The webhook settings are found under the repository’s “Settings” tab, under “Webhooks”.

Image and Cert-Manager Configuration#

image:
  repository: "summerwind/actions-runner-controller"
  actionsRunnerRepositoryAndTag: "summerwind/actions-runner:latest"
  dindSidecarRepositoryAndTag: "docker:dind"
  pullPolicy: IfNotPresent

certManagerEnabled: true
  • The certManagerEnabled: true parameter activates Cert-Manager within the Kubernetes cluster, which automates the issuance and renewal of TLS certificates for secure communications.
  • The image section outlines the Docker images used by the self-hosted GitHub Actions runner, including the controller, runner, and Docker-in-Docker sidecar images.

Install Helm Chart#

Add and update the Helm repository:

helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller
helm repo update

Fetch the chart and prepare your values file:

helm fetch actions-runner-controller/actions-runner-controller --untar --untardir ./mychart
cp ./mychart/actions-runner-controller/values.yaml ./custom-values.yaml

Modify custom-values.yaml as needed, then install:

helm install actions-runner-controller actions-runner-controller/actions-runner-controller -f custom-values.yaml

Create Runner Deployment#

If you use a private repository with SSL, you need to import your SSL certificate into a Kubernetes secret:

kubectl create secret generic ca-cert --from-file=ca.crt -n actions

Example RunnerDeployment manifest:

apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  annotations:
    karpenter.sh/do-not-evict: "true"
  name: self-hosted-runner-deployment
  namespace: actions
spec:
  template:
    spec:
      repository: githubuser/repository
      labels:
        - self-hosted-linux
      resources:
        requests:
          cpu: 1500m
          memory: 2000Mi
      volumes:
        - name: certs
          secret:
            secretName: ca-cert
            items:
              - key: ca.crt
                path: ca.crt
      dockerVolumeMounts:
        - mountPath: /etc/docker/certs.d/yourprivaterepo.intranet
          name: certs
          readOnly: true

Apply the deployment:

kubectl apply -f runnerdeployment.yml

Scaling Deployments#

You can use a HorizontalRunnerAutoscaler to automatically scale your runners:

apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
  name: self-hosted-runner-deployment-autoscaler
  namespace: actions
spec:
  maxReplicas: 30
  minReplicas: 0
  scaleTargetRef:
    kind: RunnerDeployment
    name: self-hosted-runner-deployment
  scaleUpTriggers:
  - duration: 30m
    githubEvent:
      workflowJob: {}

This configuration dynamically adjusts the number of self-hosted GitHub Actions runners between 0 and 30, scaling up in response to workflow job events.

Testing with GitHub Runners#

After installation, when you run a CI/CD pipeline, you should see your GitHub runners online and available to execute jobs.