Von 7 Minuten auf 13 Sekunden: Warum ich mein eigenes CI/CD-Tool gebaut habe

GitHub Actions ist ein fantastisches Tool. Die Integration in GitHub ist nahtlos, die Marketplace-Actions decken fast jeden Use Case ab, und für die meisten Projekte reicht es vollkommen aus. Trotzdem habe ich mein eigenes CI/CD-Tool geschrieben. Nicht weil GitHub Actions schlecht ist — sondern weil es für meine Anforderungen zu langsam war.

Das Problem mit GitHub Actions

Wer schon mal eine GitHub Actions Pipeline optimiert hat, kennt das Gefühl: Man cached Dependencies, parallelisiert Jobs, nutzt kleinere Base-Images — und am Ende wartet man trotzdem Minuten, bis der Build durch ist.

Bei manchen Projekten sah das so aus: Ursprüngliche Build-Zeiten von 30-40 Minuten. Nach intensiver Optimierung — Docker Layer Caching, Dependency Caching, Build-Matrix-Tuning — konnte ich die Zeiten auf 5-7 Minuten drücken. Klingt nach einem Erfolg, fühlt sich aber trotzdem langsam an, wenn man gerade schnell einen Fix deployen will.

Das Grundproblem dabei ist architekturbedingt und lässt sich nicht wegoptimieren:

Jeder Build startet bei Null. GitHub Actions provisioniert für jeden Workflow-Run eine frische VM oder einen Container. Das bedeutet: Go muss installiert werden. Node muss installiert werden. Docker muss gestartet werden. Dependencies müssen heruntergeladen werden. Jedes. Einzelne. Mal. Caching hilft, aber das Entpacken und Wiederherstellen eines Caches kostet ebenfalls Zeit.

Self-Hosted Runner helfen kaum. Man könnte denken, dass eigene Runner das Problem lösen. Tun sie aber nur bedingt. Die Actions selbst installieren trotzdem Tools, laden Dependencies, und der Overhead des Runners bleibt.

Lokales Testen ist nicht möglich. Will man eine Pipeline lokal testen, braucht man Third-Party-Tools wie act, die GitHub Actions nur annähernd nachbilden. Die Pipeline-Syntax ist an GitHub gebunden — man kann sie nicht einfach auf der eigenen Maschine ausführen.

Der Ansatz: Was wäre, wenn die Tools schon da wären?

Die Idee hinter cictl ist simpel: Wenn der größte Zeitfresser das Installieren von Tools und das Bootstrapping der Umgebung ist, dann eliminiere ich genau das.

cictl ist ein leichtgewichtiges CI/CD-Tool, das als dauerhafter Runner läuft. Die Tools — Go, Node, Docker, Podman — sind vorinstalliert und sofort verfügbar. Kein Download, kein Entpacken, kein Setup. Der Build startet und die erste Zeile Code kompiliert sofort.

Das Ergebnis war sofort spürbar: Dieselbe Pipeline, die bei GitHub Actions nach Optimierung 5-7 Minuten brauchte, lief mit cictl in 30-50 Sekunden. Nach weiteren Anpassungen bin ich jetzt bei 13-16 Sekunden für einen kompletten Build inklusive Container-Image und Push in die Registry.

Wo die Zeit gespart wird

Die Zeitersparnis kommt aus mehreren Faktoren:

Kein Tool-Setup. Go, Docker, Node — alles ist bereits installiert und konfiguriert. Das Docker-Image, in dem der Runner läuft, enthält genau die Tools, die für die jeweiligen Projekte gebraucht werden. Nicht mehr, nicht weniger.

Persistente Umgebung. Der Runner läuft dauerhaft. Go-Module sind im Cache, Docker-Layer sind lokal vorhanden, npm-Packages müssen nicht jedes Mal neu heruntergeladen werden. Die Umgebung wärmt sich mit der Zeit auf.

Kein Orchestrierungs-Overhead. Es gibt keine VM-Provisionierung, kein Container-Scheduling für den Runner selbst, keinen Checkout-Action-Overhead. Ein Git-Webhook kommt rein, der Code wird gepullt, der Build startet.

Schlanke Pipeline-Definition. Keine marketplace Actions mit eigenem Setup. Die .cictl.yaml beschreibt direkt, was passieren soll:

name: my-app

version:
  strategy: semver
  source: conventional-commits

build:
  container:
    image: ghcr.io/org/my-app
    tags:
      - "v{{.Version}}"
      - latest
    push: true

on:
  push:
    branches:
      - main

jobs:
  test:
    name: Tests
    runs-on: local
    steps:
      - name: Test
        run: go test ./...

  lint:
    name: Lint
    runs-on: local
    steps:
      - name: Vet
        run: go vet ./...

Kein actions/checkout@v4, kein actions/setup-go@v5, kein docker/login-action@v3. Nur die eigentlichen Build-Schritte.

Setup und Einrichtung

cictl lässt sich auf zwei Arten betreiben: lokal auf dem eigenen Rechner oder als Runner im Kubernetes-Cluster.

Lokale Ausführung

Pipeline lokal ausführen — etwas, das mit GitHub Actions schlicht nicht geht:

cictl run -f .cictl.yaml

Das war's. cictl liest die Pipeline, löst die Version auf, führt die Jobs aus. Auf der eigenen Maschine, mit den lokal installierten Tools.

Runner im Cluster

Für den dauerhaften Betrieb wird der Runner mit einer Konfigurationsdatei gestartet:

repos:
  - url: https://github.com/org/repo.git
    branch: main

webhook:
  enabled: true
  listen: ":8080"
  secret: "${WEBHOOK_SECRET}"

store:
  type: redis

Der Runner überwacht die konfigurierten Repositories, reagiert auf GitHub/GitLab-Webhooks, und stellt ein Web-Dashboard mit Live-Build-Logs bereit. Die Builds laufen automatisch — Push auf main, Webhook kommt rein, Build startet, Image wird gebaut und gepusht.

Mehrere Runner-Instanzen teilen sich den State über Redis, inklusive Session-Daten für die optionale OIDC-Authentifizierung des Dashboards.

Versionierung inklusive

Ein Feature, das ich nicht mehr missen möchte: Automatische semantische Versionierung über Conventional Commits. Kein manuelles Tag-Setzen, kein Versions-Bumping in Dateien. cictl liest die Commit-History, bestimmt die nächste Version, taggt das Repository und injiziert die Version als Variable in den Build.

Ein feat: Commit bumpt die Minor-Version, ein fix: die Patch-Version, ein Breaking Change die Major-Version. Das passiert automatisch, bei jedem Build.

Fazit

GitHub Actions bleibt ein gutes Tool für viele Teams und Projekte. Aber wenn Build-Geschwindigkeit wirklich zählt — wenn der Unterschied zwischen 7 Minuten und 13 Sekunden den Entwicklungsflow verändert — dann lohnt es sich, die CI/CD-Pipeline näher an die eigene Infrastruktur zu bringen.

cictl ist kein GitHub-Actions-Ersatz für alle. Es ist ein spezialisiertes Tool für Teams und Entwickler, die maximale Build-Performance wollen und bereit sind, ihre Runner selbst zu betreiben. Dafür bekommt man Builds, die sich anfühlen wie lokale Ausführung — weil sie es im Grunde auch sind.

GitHub Actions (optimiert) cictl Runner
Blog-Build 5-7 min 13-16 sek
Tool-Installation Jedes Mal Einmalig vorinstalliert
Lokale Ausführung Nicht nativ cictl run
Setup-Overhead VM/Container pro Run Dauerhafter Runner