CI/CD auf Bare Metal: Wie wir unsere Deployment-Zeiten um bis zu 85 % verkürzt und die Kosten halbiert haben

Instabile Pipelines in Cloud-VMs? Damit haben wir lange gekämpft. Inzwischen läuft unsere gesamte CI-Infrastruktur auf Bare Metal – mit klaren Vorteilen: schnellere und stabilere Pipelines, kürzere Wartezeiten, bessere Developer Experience. Und das Beste: Wir zahlen nur noch die Hälfte.

:(){ :|:& };:

Wolfgang Medina-Erhardt
DevOps Engineer bei punkt.de, Experte für Bare-Metal-Deployments und Automatisierung von Entwicklungs- und Betriebsprozessen.

challenge accepted

Maik Peuser
DevOps Engineer bei punkt.de, spezialisiert auf CI/CD-Pipelines und Infrastrukturoptimierung mit Fokus auf Performance und Effizienz.
Lesedauer: ca. 5 Minuten

Wie viele andere Agenturen setzen wir CI/CD in unseren Projekten ein. Das Prinzip ist simpel: Entwickler schreiben Code, pushen ihn nach GitLab, und nach jedem Push laufen automatisierte Build- und Test-Jobs – z. B. statische Codeanalyse, Akzeptanztests, usw.

Sobald ein Merge in den Main-Branch oder ein Tag erfolgt, starten automatische Deployments in Test- und Produktionsumgebungen.

Ein Diagramm, das einen Git-Workflow darstellt, bei dem der Code in ein Get-Repository übertragen und dann automatisch getestet und bereitgestellt wird

Wie lief es bisher?

Für unsere CI-Jobs nutzten wir virtuelle Maschinen von Hetzner Cloud. Die Plattform bietet eine gute API, eigene Tools für GitLab CI und ist "Made in Europe".

Unsere Infrastruktur bestand aus drei Teilen:

  • GitLab-Server, auf den der Code committed wird
  • Control Node, der Jobs entgegennimmt und für jeden Job eine kurzlebige Hetzner-VMs erstellt
  • Die Runner-VMs, die kurz für einen Job laufen und danach wieder abgeräumt werden
Ein Diagramm, das eine Gitlab-Runner-Infrastruktur mit einem Gitlab-Server, einem Runner-Kontrollknoten und einer Flotte flüchtiger Runner darstellt

Um die Anlaufzeit beim Start einer VM zu umgehen, hielten wir tagsüber einen Pool von VMs im Standby. Das Setup war lange stabil und skalierbar.

Was ging schief?

Mit Hetzners wachsender Popularität stieg auch die Last auf deren Infrastruktur. Mehr und mehr CI-Jobs scheiterten schlicht an fehlenden Ressourcen in den Hetzners Rechenzentren – teilweise nach minutenlangem Warten, was extrem frustrierend war.

Kein Vorwurf an Hetzner – sie wurden schlicht Opfer ihres eigenen Erfolgs.

Auch die Performance-Limits der Cloud-Instanzen machten sich bemerkbar: Full-Stack-Tests brauchten bis zu 15 Minuten, manche Testszenarien stürzten mit OOM-Fehlern ab.

Hinzu kam, dass docker-machine, die Software zum Verwalten der Hetzner-VMs, mittlerweile deprecated ist und durch Fleeting ersetzt werden soll – ein neues, pluginbasiertes Produkt, das von GitLab selbst entwickelt wird.

Ein Umstieg auf Fleeting hätte unser Grundproblem jedoch nicht gelöst – auch die Entwickler des Hetzner-Plugins für Fleeting kämpfen mit dem Problem von den beschränkten Ressourcen.

Welche Alternativen gab es?

Public Cloud

Kurzzeitig haben wir erwogen, in die Public Cloud zu wechseln – also GCP, Azure oder AWS. Alle drei Anbieter werden offiziell von Gitlabs Fleeting-Architektur unterstützt, haben eine ausgereifte API für VM-Management und sind durch viele andere Agenturen im Einsatz erprobt.

Letztlich haben wir uns jedoch dagegen entschieden, und zwar aus folgenden Gründen:

  • Die Kosten in der Public Cloud sind intransparent und vermutlich höher als bei unserer bisherigen Hetzner-Lösung
  • Auch wenn die Bereitstellung von VMs in der Public Cloud stabiler sein könnte (größere Ressourcenpools), bleibt das Provisionieren von VMs grundsätzlich ein langsamer Prozess
  • Alle drei großen Anbieter sind US-basiert – wir bevorzugen einen Anbieter innerhalb der EU
Der Zeitungsausschnitt von Abe Simpson, der wütend seine Faust erhebt, unter der wörtlichen Schlagzeile „Old Man Yells at Cloud“

Kubernetes

Als ITler wissen wir: Fast jedes Problem lässt sich mit Kubernetes lösen 🙂

Trotzdem haben wir uns dagegen entschieden.

Im Alltag arbeiten wir mit einem völlig anderen Stack. Unser Tagesgeschäft sind FreeBSD- und Linux-Server – Container nutzen wir fast ausschließlich in lokalen Entwicklungsumgebungen oder für spezielle Edge-Cases.

Eine Kubernetes-basierte CI-Infrastruktur neu aufzusetzen, hätte eine steile Lernkurve, erheblichen Zeitaufwand sowie zusätzlichen Setup- und Wartungsaufwand bedeutet.

Ein Dilbert-Comicstrip mit zwei Figuren, von denen die eine sagt: „ You can't solve a problem by just saying techy things“, und die andere antwortet: „Kubernetes.“

Inspiration: TYPO3 Core Testing Infrastructure

Unzufrieden mit den Alternativen sprachen wir mit TYPO3-Core-Entwickler Stefan Bürk über deren Test-Infrastruktur, ihre Entstehung und die Herausforderungen dabei.

Auf den ersten Blick wirkt ihr Ansatz fast altmodisch: statt Cloud-VMs nutzt TYPO3.org Runner auf dedizierten Bare-Metal-Servern.

Der Effekt: keine Wartezeiten für VM-Provisioning und genug Power, um hunderte von CI-Tests kontinuierlich auszuführen.

Anstelle von Docker setzen sie auf Podman, ein OCI-Runtime mit entscheidenden Vorteilen:

  • kein Daemon nötig, dadurch verschachtelte Setups (z. B. podman-in-podman) einfacher – wichtig für unsere Full-Stack-Tests
  • läuft standardmäßig als unprivilegierter User – sicherer, kleinere Angriffsfläche

Infrastruktur-Umstellung

Inspiriert von TYPO3 mieteten wir einen dedizierten AX102-Server bei Hetzner: 128 GB RAM, 16 CPU-Kerne.

Statt GitLab-Server + Control-Node + Cloud-VMs gibt es nun: GitLab-Server + einen leistungsstarken Runner.

Ein Diagramm, das eine Gitlab-Runner-Infrastruktur mit einem Gitlab-Server und einem Runner abbildet

Das bringt sofortige Vorteile:

  • Caches sind direkt verfügbar und müssen nicht zwischen Server und VMs kopiert werden
  • Images werden einmalig gepullt und stehen sofort allen Jobs zur Verfügung

Technische Unterschiede

TYPO3.org stellt seine Core Testing Infrastructure der Community als öffentlichen Service zur Verfügung. Jeder Contributor kann die CI-Infrastruktur nutzen, um seinen Code zu testen und zu validieren.

Dadurch sind natürlich eine Reihe von Sicherheitsmaßnahmen notwendig:

  • Es dürfen nur eine begrenzte Anzahl vertrauenswürdiger OCI-Images verwendet werden
  • Der Speicherbereich für die OCI-Images ist im Runner nur read-only eingebunden – vorhandene Images können also nicht verändert werden

Neben diesen Sicherheitsaspekten liegt ein starker Fokus auf einem reproduzierbaren Test-Workflow, der sowohl lokal als auch in der CI laufen kann.

Dafür gibt es ein einheitliches Testskript namens ./runTests.sh, das auch auf Entwickler-Rechnern ausgeführt werden kann.

Diese Maßnahmen funktionieren im Kontext der TYPO3-Testinfrastruktur sehr gut, da dort ausschließlich ein einziges Softwareprojekt (TYPO3) innerhalb klar definierter Umgebungen getestet wird.

Bei punkt.de hingegen nutzen wir unsere CI, um eine Vielzahl an Projekten auf Basis unterschiedlicher Technologien zu bauen und zu testen – darunter natürlich TYPO3, aber auch Neos, Keycloak, Sylius und Ansible.

Eine Beschränkung auf bestimmte OCI-Images und die Nutzung eines einheitlichen Testskripts würde in unserem Fall jedoch bedeuten, zahlreiche Ausnahmen einzubauen und eine Menge Edge-Cases für die unterschiedlichen Projekte und Technologien manuell abzufangen.

Lösung: GitLab CI/CD Components

Wie es der Zufall wollte, hatten wir bereits einen Großteil unserer CI-Pipelines auf GitLab CI/CD Components migriert.

Anstatt für jedes Projekt eine komplett neue Pipeline zu schreiben, können wir so einheitliche, wiederverwendbare und gleichzeitig individuell anpassbare Bausteine definieren, die in jedes Projekt importiert werden können.

Beispiele: Wir haben eigene CI Components für composer, npm, php-stan, php-cs usw.

Dadurch war der Umstieg auf Podman im Wesentlichen nur eine Frage, diese Components auf Podman anzupassen. In den einzelnen Projekten selbst waren somit nur minimale Änderungen notwendig.

Herausforderungen

Full-Stack-Tests

Unsere Full-Stack-Tests laufen mit docker-compose. Podman bietet zwar podman-compose, aber dieses ist nicht vollständig kompatibel.

Fehlende Features: Healthchecks und der --wait-Parameter. Lösung: zusätzliche docker-compose.override.yml, in denen Healthchecks und service_healthy-Dependencies deaktiviert werden, z.B.

services:
  php:
    depends_on: !reset null
  postgres_schema_modify: !reset null
  keycloak:
    depends_on: !reset null

Da wir uns nicht auf Healthchecks verlassen konnten, setzten wir auf einfachere Methoden, etwa wiederholte curl-Aufrufe gegen Endpunkte, bis diese „ready“ zurücklieferten.

Abgesehen davon konnten wir die Tests mit nur geringen Anpassungen auf Podman umstellen.

Podman 4.x vs. 5.x

In den Ubuntu/Debian-Repositories war zum Zeitpunkt des Setups nur Podman 4.x verfügbar – mit bekannten Bugs bei der DNS-Auflösung. Wir mussten Domain-Namen hartkodieren, um Probleme zu vermeiden.

Die Bugs sind in Podman 5.x behoben, das allerdings erst in Ubuntu 25.04 enthalten ist. Unsere Lösung: Pakete aus 25.04 einbinden und die Podman-abhängigen Pakete pinnen. Nicht ideal, aber funktional.

Ergebnisse

Der Umstieg hat sich gelohnt – die Zahlen sprechen für sich:

  • Next.js-Projekt (Build, Tests, Push ins externe Registry): 26 → 8 Minuten
  • Großes Neos-Projekt (PHP & NodeJS mit Full-Stack-Tests): 20 → 3 Minuten
  • TYPO3-Projekt (~600 Full-Stack-Tests mit Codeception): 25 → 5 Minuten

Und am wichtigsten: keine instabilen Jobs mehr durch Ressourcenmangel.

Kosten

Vorher zahlten wir zwischen 200 € und 240 € pro Monat für die Cloud-Infrastruktur.
Jetzt: 104 € für einen AX102-Runner – mit deutlich mehr Leistung.

Die Maschine ist bis heute weit davon entfernt, an ihre Grenzen zu stoßen.

Einschränkungen

Natürlich ist auch dieses Setup nicht perfekt. Dinge, die man bedenken muss:

  • keine Cloud-spezifischen Features (Firewalls, Load Balancing, Networking)
  • ein einzelner Runner = Single Point of Failure (lösbar mit mehreren Maschinen)
  • Skalierung nur in größeren Schritten (104 → 208 € usw.)
  • podman-compose ist nicht vollständig docker-compose-kompatibel
  • Große Public Cloud-Anbieter wie Azure, AWS und GCP sind offiziell von GitLab unterstützt und ggf. einfacher einzurichten

Vorteile

Dem stehen klare Pluspunkte gegenüber:

  • keine Wartezeit durch VM-Provisioning
  • halber Preis, deutlich mehr Leistung
  • theoretisch auch On-Premises mit eigener Hardware möglich
  • dank GitLab CI Components nur minimale Pipeline-Anpassungen nötig

Fazit

Nach einigen Monaten im Einsatz sind wir überzeugt: Der Wechsel von Cloud-VMs zu Bare Metal hat sich absolut gelohnt.

Um anderen den Umstieg zu erleichtern, haben wir die Ansible-Rolle, mit der wir unseren GitLab Runner eingerichtet haben, veröffentlicht. Probiert sie aus, richtet eure eigenen Runner ein und gebt uns Feedback.

Und wenn ihr ebenfalls eure Pipelines beschleunigen und gleichzeitig Kosten reduzieren möchtet, bieten wir auch GitLab-Consulting an – kontaktiert uns gerne für Details.

Dank an

  • Stefan Bürk
  • Christian „Lolli“ Kuhn
Grafik: Ansprechpartner Wolfgang Medina-Erhardt
Bei Fragen zu eurem Gitlab, sprecht uns gerne an!
Wir beraten gerne auch bei euch um eure Development Infrastruktur zu beschleunigen
Wolfgang Medina-Erhardt
DevOps Specialist
+49(0)721 91090
Jetzt kontaktieren
Teilen:

Weitere Beiträge

Alles beginnt mit dem ersten Schritt.
Chris Garmatz, Entwicklung bei punkt.de
Arbeiten bei punkt.de