kubevirt-actions-runner is a custom GitHub Actions runner image designed for use with the Actions Runner Controller (ARC).
This runner provisions ephemeral virtual machines (VMs) using KubeVirt, extending the flexibility and security of your CI/CD workflows.
This is particularly useful for validating scenarios that are not supported by default GitHub-hosted runners, such as:
- Running Windows or macOS jobs
- Custom environments that require specific kernel modules or system services
- Jobs requiring strong isolation from the host system
- Ephemeral VM creation: Launch a fresh VM for every job and destroy it after completion
- Increased isolation: Ideal for untrusted code or complex system configurations
- Custom system-level configuration support
- Easily integrates with ARC and Kubernetes-native tooling
To use this project, ensure you have the following installed:
- A working Kubernetes cluster
- Actions Runner Controller
- KubeVirt
This template will be used to spawn VMs on-demand for each GitHub job. The base VirtualMachine will never be started; instead, clones of it will be launched as VirtualMachineInstances.
# Create namespace if it doesn't exist
! kubectl get namespace "${namespace}" && kubectl create namespace "${namespace}"
# Apply the VM template
kubectl apply -f scripts/vm_template.yml -n "${namespace}"Example snippet from vm.yaml:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: ubuntu-jammy-vm
spec:
runStrategy: Manual
template:
spec:
domain:
devices:
filesystems:
- name: runner-info
virtiofs: {}The runner-info volume is mounted at runtime and contains metadata required by the GitHub Actions runner, e.g.:
{
"name": "runner-abcde-abcde",
"token": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"url": "https://github.com/org/repo",
"ephemeral": true,
"groups": "",
"labels": ""
}The service account used by the runner pods must be granted permissions to manage KubeVirt VMs.
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubevirt-actions-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: kubevirt-actions-runner
rules:
- apiGroups: ["kubevirt.io"]
resources: ["virtualmachines"]
verbs: ["get", "watch", "list"]
- apiGroups: ["kubevirt.io"]
resources: ["virtualmachineinstances"]
verbs: ["get", "watch", "list", "create", "delete"]
- apiGroups: ["cdi.kubevirt.io"]
resources: ["datavolumes"]
verbs: ["get", "watch", "list", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cdi-cloner
rules:
- apiGroups: ["cdi.kubevirt.io"]
resources: ["datavolumes/source"]
verbs: ["create"]Use Helm to install the GitHub Actions runner scale set.
Create a values.yml file:
githubConfigUrl: https://github.com/<your_enterprise/org/repo>
githubConfigSecret: <your_github_secret>
template:
spec:
serviceAccountName: kubevirt-actions-runner
containers:
- name: runner
image: electrocucaracha/kubevirt-actions-runner:latest
command: []
env:
- name: KUBEVIRT_VM_TEMPLATE
value: ubuntu-jammy-vm
- name: RUNNER_NAME
valueFrom:
fieldRef:
fieldPath: metadata.nameInstall using Helm:
helm upgrade --create-namespace --namespace "${namespace}" \
--wait --install --values values.yml vm-self-hosted \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-setflowchart TD
subgraph GitHub
A[GitHub Job Triggered]
end
subgraph Kubernetes Cluster
B[Actions Runner Controller]
C[Create kubevirt-actions-runner Pod]
E[Mount runner-info.json]
F[Create VirtualMachineInstance from Template]
G[Boot VirtualMachineInstance]
end
subgraph Virtual Machine
H[Runner fetches GitHub job metadata]
I[Execute Job Steps]
J[Job Completion]
end
subgraph Teardown
K[Cleanup: Terminate VMI & Runner Pod]
end
A --> B --> C --> E --> F --> G --> H --> I --> J --> K
- macOS support: macOS virtualization is not supported via KubeVirt due to licensing constraints.
- Long job durations: Boot time of VMs may increase total runtime.
- Persistent state: Not designed for workflows requiring persisted state between jobs.
Contributions are welcome! Please open issues or submit PRs to help improve this project.
This project includes Dev Container support, making it easy to get started with a fully configured development environment. This ensures consistency across development setups and allows contributors to begin working without manual environment configuration.
You can launch a cloud-based development environment instantly:
- Install Visual Studio Code.
- Install the Dev Containers extension.
- Ensure you have a container runtime installed and running:
- Docker Desktop (Windows/macOS)
- Docker Engine or Podman (Linux)
- Open this repository in Visual Studio Code.
- When prompted, reopen the project in a Dev Container.
KCD Guadalajara 2025 – Migrating GitHub Actions with Nested Virtualization to the Cloud-Native Ecosystem
This project was presented at Kubernetes Community Days (KCD) Guadalajara 2025, showcasing how to extend GitHub Actions with KubeVirt and nested virtualization to support custom and complex CI/CD workflows in Kubernetes.
The presentation walks through:
- Challenges with standard GitHub-hosted runners
- Benefits of using KubeVirt for GitHub Actions runners
- Live demo deploying and running jobs inside ephemeral VMs
- Lessons learned and architectural considerations