Was ist ein Forgejo?
Forgejo ist eine beliebte, quelloffene Git-Hosting-Lösung, die sich hervorragend für Teams eignet, die ihre Infrastruktur lieber selbst verwalten. Seit Einführung von Forgejo Actions ist es möglich, CI/CD-Jobs direkt über Forgejo auszuführen – ähnlich wie bei GitHub Actions.
In diesem Beitrag zeige ich dir, wie du einen Forgejo Runner auf Kubernetes betreibst, der vollständig Docker-fähig ist und sich automatisch bei deiner Forgejo-Instanz registriert.

Inhaltsverzeichnis
Ursprung von Forgejo
Forgejo ist ein Fork von Gitea, der aus einer Auseinandersetzung innerhalb der Gitea-Community entstanden ist. Die Hintergründe sind sowohl technisch als auch organisatorisch motiviert. Hier die wichtigsten Punkte zur Entstehung und dem „Warum“:
Gitea war ursprünglich ein Community-getriebener Fork von Gogs und entwickelte sich zu einer beliebten, leichtgewichtigen Git-Hosting-Lösung in Go. Im Jahr 2022 kündigte das Gitea-Projekt an, dass eine neue Firma namens Gitea Ltd. gegründet wurde, um die Weiterentwicklung zu kommerzialisieren. Diese Entscheidung wurde von vielen Mitgliedern der Community kritisch gesehen, vor allem weil:
- Die Entscheidung ohne breiten Konsens getroffen wurde.
- Die Kontrolle über Domain, Infrastruktur und Repositories auf die Firma überging.
- Es Bedenken über die Offenheit und Governance des Projekts gab.
Als Reaktion darauf wurde Forgejo Ende 2022 als Fork ins Leben gerufen. Ziel war es, die ursprünglichen Ideale von Gitea als gemeinschaftlich entwickeltes, nicht kommerzielles Projekt wiederzubeleben.
Forgejo basiert weiterhin auf Gitea, entwickelt sich aber unabhängig und legt Wert auf:
- Community Ownership
- Transparente Governance
- Freiheit von kommerziellem Einfluss
- Unterstützung durch Organisationen wie Codeberg.org, die auf freie Software setzen
🚀 Warum ein eigener Runner?
- Unabhängigkeit von externen Providern
- Volle Kontrolle über CI-Umgebungen
- Unterstützung für Docker-in-Docker Workflows
- Optimale Integration in bestehende Kubernetes-Cluster
🧩 Architektur des Deployments
Namespace
apiVersion: v1kind: Namespacemetadata: name: forgejo-runner
Deployment
apiVersion: apps/v1kind: Deploymentmetadata: name: forgejo-runner namespace: forgejo-runnerspec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: forgejo-runner app.kubernetes.io/part-of: forgejo-runner strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 template: metadata: labels: app.kubernetes.io/name: forgejo-runner app.kubernetes.io/part-of: forgejo-runner spec: restartPolicy: Always volumes: - name: docker-certs emptyDir: {{}} - name: runner-data emptyDir: {{}} - name: runner-config configMap: name: forgejo-runner-config items: - key: "config.yaml" path: "config.yaml" - name: runner-secret secret: secretName: runner-secret initContainers: - name: runner-register image: code.forgejo.org/forgejo/runner:7.0.0 command: - bash - -c - | set -euo pipefail env exec forgejo-runner register --no-interactive --token "$(cat "/runner-auth/token")" --name "$RUNNER_NAME" "--instance" "$FORGEJO_INSTANCE_URL" env: - name: RUNNER_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: FORGEJO_INSTANCE_URL value: https://git.bueraner.de resources: requests: cpu: "0.25" memory: "64Mi" limits: cpu: "0.50" memory: "64Mi" volumeMounts: - name: runner-data mountPath: /data - name: runner-config mountPath: /opt readOnly: true - name: runner-secret mountPath: /runner-auth readOnly: true containers: - name: runner image: code.forgejo.org/forgejo/runner:7.0.0 command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; forgejo-runner daemon --config /opt/config.yaml"] env: - name: DOCKER_HOST value: tcp://localhost:2376 - name: DOCKER_CERT_PATH value: /certs/client - name: DOCKER_TLS_VERIFY value: "1" volumeMounts: - name: docker-certs mountPath: /certs - name: runner-data mountPath: /data - name: runner-config mountPath: /opt readOnly: true - name: daemon image: code.forgejo.org/oci/docker:dind env: - name: DOCKER_TLS_CERTDIR value: /certs securityContext: privileged: true volumeMounts: - name: docker-certs mountPath: /certs
kustomization.yaml
Ich nutze flux um meinen k3s-Cluser zu managen.
Hier wird in der kustomization.yaml
die values.yaml
als configMap
zur Verfügung gestellt.
apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomization
resources: - ./namespace.yaml - ./sealed-secret.yaml - ./deployment.yaml
configMapGenerator: - name: forgejo-runner-config files: - "config.yaml"
namespace: forgejo-runner
config.yaml
runner: envs: DOCKER_HOST: tcp://localhost:2376 DOCKER_TLS_VERIFY: 1 DOCKER_CERT_PATH: /certs/client labels: - 'docker:docker://ghcr.io/catthehacker/ubuntu:act-22.04' - 'self-hosted:host://-self-hosted' - 'ubuntu-20.04:docker://ghcr.io/catthehacker/ubuntu:act-20.04' - 'ubuntu-22.04:docker://ghcr.io/catthehacker/ubuntu:act-22.04' # - 'ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-latest' - 'runner-20.04:docker://ghcr.io/catthehacker/ubuntu:runner-20.04' - 'runner-22.04:docker://ghcr.io/catthehacker/ubuntu:runner-22.04' - 'runner-latest:docker://ghcr.io/catthehacker/ubuntu:runner-latest'container: network: host options: -v /certs/client:/certs/client valid_volumes: - /certs/client docker_host: ""
Secret
HINWEIS Das Secret runner-secret
musst du natürlich noch erstellen!