- Makefile 60.7%
- Go Template 39.3%
| templates | ||
| .helmignore | ||
| Chart.yaml | ||
| CLAUDE.md | ||
| Makefile | ||
| README.md | ||
| values.yaml | ||
forge-runner-k8s Helm Chart
Deploys a Forgejo Actions runner on Kubernetes. CI jobs run as short-lived Pods rather than Docker containers; image builds are handled by a rootless BuildKit sidecar.
Prerequisites
- Kubernetes cluster with a ReadWriteMany StorageClass (NFS, CephFS, etc.)
- Helm 3.8+
- Forgejo instance with Actions enabled
1 — Register the runner on the Forgejo server
Run the following on the host where Forgejo is installed. This pre-registers the runner and emits the credentials you will pass to the chart.
forgejo forgejo-cli actions register
The command prints a UUID and a 40-character hex token. Keep both — you need them in the next step.
Alternatively, generate the registration from the Forgejo web UI: Site Administration → Runners → Create new runner (or the equivalent repo/org settings page).
2 — Install the chart
Create a values file (keep it out of version control):
# my-values.yaml
forgejo:
instanceUrl: "https://git.example.com"
runner:
connection:
uuid: "<uuid from step 1>"
token: "<token from step 1>"
dockerk8s:
registry: "registry.example.com/ci"
registryPushSecret: "registry-push-secret" # pre-existing dockerconfigjson Secret
persistence:
storageClass: "nfs-rwx"
Then install:
helm install my-runner . -f my-values.yaml -n ci --create-namespace
Check the runner pod comes up:
kubectl -n ci get pods
kubectl -n ci logs deploy/my-runner-forge-runner -c runner
The runner should appear as online in the Forgejo runners list within a few seconds.
Key values reference
| Value | Required | Description |
|---|---|---|
forgejo.instanceUrl |
yes | URL of the Forgejo instance |
forgejo.runner.connection.uuid |
yes | UUID from forgejo-cli actions register |
forgejo.runner.connection.token |
yes | 40-char hex token from the same command |
dockerk8s.registry |
yes | Registry prefix for images built by CI jobs |
dockerk8s.registryPushSecret |
for builds | dockerconfigjson Secret for push auth |
dockerk8s.imagePullSecrets |
if private images | Comma-separated pull secret names for job Pods |
dockerk8s.build.enabled |
no | Expose a restricted, docker build-only Docker API listener to CI job Pods (via a ClusterIP Service + NetworkPolicy carve-out); no separate docker push needed, builds push automatically |
persistence.storageClass |
yes | Must support ReadWriteMany |
networkPolicy.enabled |
no | Isolate the runner pod and CI job pods |
dockerk8s.jobIsolation.mountReservedDirs |
no | PVC directories (cache, builds) a job mount can never reach — see CLAUDE.md |
dockerk8s.jobIsolation.fsGroup |
no | fsGroup applied to job Pods; not a read-isolation boundary between jobs |
dockerk8s.jobIsolation.workspaceCleanup |
no | Remove a job's workspace directory once no container still references it |
dockerk8s.jobResources.requests.cpu/.memory |
no | Default CPU/memory request applied to a job Pod when the job didn't request one |
dockerk8s.jobResources.limits.cpu/.memory |
no | Default CPU/memory limit applied to a job Pod when the job didn't request one |
dockerk8s.jobResources.max.cpu/.memory |
no | Absolute CPU/memory limit ceiling; a resolved value above this is clamped down to it |
Note on multi-tenancy: the shared PVC and jobIsolation settings reduce blast radius between jobs but do not provide hard read-isolation on their own (all job Pods share one PVC and commonly run as the same UID). For multi-tenant environments, install one release — its own namespace and PVC — per tenant.
Upgrade
helm upgrade my-runner . -f my-values.yaml -n ci
Uninstall
helm uninstall my-runner -n ci
The PVC is not deleted automatically. Remove it manually if no longer needed:
kubectl -n ci delete pvc my-runner-forge-runner