Kai Ole Hartwig
9 Min. Lesezeit
Niedrig

Halber Sieg: Warum ich Renovates PHP/Composer-Installation nur zur Hälfte von GitHub lösen konnte

Es gibt zu diesem Beitrag ein Gegenstück, in dem alles glattläuft: Ich habe OpenTofus Provider-Downloads von GitHub gelöst, indem ich sie in eine selbst betriebene Registry umgepackt habe — seitdem juckt es tofu init nicht mehr, ob GitHub gerade einen schlechten Tag hat. Sauberer Bogen, gutes Ende.

Das hier ist die andere Sorte Geschichte. Dieselbe Krankheit — ein CI-Job, der hart scheitert, sobald ein GitHub-Download in ein Timeout läuft. Derselbe Reflex — spiegeln. Nur wurde aus der für Provider so sauberen Lösung bei Renovate etwas Komplizierteres, und selbst als sie funktionierte, brachte sie mir nur die Hälfte dessen, was ich eigentlich wollte. Genau dieser Halbpunkt ist der interessante Teil.

Nachtrag, September 2026: Renovate habe ich inzwischen durch ein eigenes Werkzeug ersetzt. Die Befunde zu containerbase gelten für Renovate-Setups unverändert.

01 — Der naheliegende Schritt: einfach wiederholen, was letztes Mal funktioniert hat

Renovate installiert php und composer zur Laufzeit über containerbase, das vorgebaute Tarballs von github.com/containerbase/*-prebuild herunterlädt. GitHub ist IPv4-only und unter Last unzuverlässig — ein schlechter Nachmittag dort reichte, um jeden geplanten Scan über einen Composer-Download rot werden zu lassen. Lehrbuchfall. Also die Prebuilds in die eigene Registry spiegeln, containerbase auf den Mirror zeigen lassen, fertig — genau wie bei den Providern.

Zwei Dinge weigerten sich sofort, genauso zu laufen wie bei den Providern.

02 — Erste Komplikation: Das sind keine Artefakte, das sind Dateien

Provider wurden bei mir zu OCI-Artefakten in der Container-Registry — die Registry spricht OCI, also passten sie. containerbase tickt simpler und zugleich unflexibler: ein einfacher GET-Request, mit genau einem Hebel — einen URL-Präfix austauschen (URL_REPLACE). OCI spricht das Tool nicht. Es will einen schlichten Dateiserver, mit derselben URL-Form wie GitHub Releases.

Die angenehme Überraschung: GitLabs generische Package-Registry ist exakt das, auf demselben Host, den ich ohnehin schon betreibe, dual-stack — und ihre URL-Form .../packages/generic/<name>/<version>/<file> passt so sauber auf containerbases <tool>-prebuild/releases/download/<ver>/<file>, dass ein reiner Präfix-Tausch reicht. Um den exakten Dateinamen herauszufinden, den containerbase wollte (welche Ubuntu-Variante? welche Architektur?), zeigte ich URL_REPLACE testweise auf einen frei erfundenen Host und las die Fehlermeldung:

Die Probe (install-tool php 8.5.8)

 

# URL_REPLACE_0_TO = probe.invalid/cb/  → der Fehler nennt das Asset
ERROR: getaddrinfo ENOTFOUND probe.invalid
  url: "https://probe.invalid/cb/php-prebuild/releases/download/
        8.5.8/php-8.5.8-jammy-aarch64.tar.xz.sha512"

 

Das Image läuft auf Ubuntu Noble — containerbase fragt aber nach dem Jammy-Prebuild. Das lieber empirisch feststellen, statt zu raten.

03 — Zweite Komplikation: Ein bewegliches Ziel lässt sich nicht vorab befüllen

Das hier war die Komplikation, die wirklich zählte. Bei Providern pinne ich die Versionen selbst — ich entscheide, dass es aws 6.52.0 ist, ich spiegle aws 6.52.0, die Kopplung liegt vollständig in meiner Hand. Renovate läuft dagegen mit binarySource=install — es installiert php und composer bei Bedarf, in genau der Version, die das jeweilige Repository gerade braucht. Das Versions-Set gehört nicht mir. Es ist, was rund 70 Repos zufällig brauchen. Nur eine Version zu spiegeln reicht dann genau bis zum ersten Repo, das eine andere verlangt — dort steht ein 404 mitten im Scan.

Lehre eins

„Einfach spiegeln“ setzt voraus, dass man weiß, was „es“ eigentlich ist. Entscheiden Consumer über das Versions-Set und nicht man selbst, ist Vorab-Befüllung kein Mirror mehr — sondern eine Wette.

Bevor ich also weiteren Code schrieb, kam der unglamouröse Teil: die composer.json jedes verwalteten Repos lesen und zusammentragen, was die php-Installation tatsächlich steuert — zuerst config.platform.php, dann die require.php-Bandbreite.

Was die php-Installation pinntAnzahlLöst auf zu
require.php: ^8.548neueste → 8.5.8
require.php: ^8.29neueste → 8.5.8
require.php: ^8.3 / >=8.2 / …8neueste → 8.5.8
config.platform.php: 8.5.05exakt → 8.5.0
config.platform.php: 8.51→ 8.5.8
composer/composer gepinnt0immer neueste → 2.10.2

73 composer.json-Dateien über development, devops und ai-ready-platform hinweg. Jede offene Bandbreite löst auf dieselbe neueste Version auf — nur exakte platform.php-Pins entkommen dem.

Die Panik („ich werde Dutzende Versionen spiegeln und jeder Constraint-Änderung hinterherjagen müssen“) löste sich damit in Luft auf. Das tatsächliche Set war winzig: php 8.5.8 (jede Bandbreite landet hier), php 8.5.0 (fünf Repos pinnen exakt darauf), und composer 2.10.2 (niemand pinnt composer). Drei Artefakte. Das bewegliche Ziel bewegt sich kaum — nur wusste ich das erst, nachdem ich gezählt hatte, statt eine Vermutung zu spiegeln und auf den 404 zu warten.

Der Rest ist damit reine Verrohrung: Ein Job zieht diese drei einmalig von GitHub und pusht sie in die generische Package-Registry; Renovates URL_REPLACE zeigt darauf (Job-Token als Basic-Auth in der URL, weil containerbase keinen Header setzen kann); und weil Renovate die Versionen pro Repo während des Scans installiert, nicht nur einmal vorab, bleibt die Umleitung für den gesamten Job aktiv. Der erste echte geplante Scan nach dem Rollout installierte php und composer direkt aus dem Mirror, über IPv6, ohne 404. Es funktioniert.

04 — Der halbe Sieg: Eine Abhängigkeit zu töten ist nicht dasselbe wie den Pool zu wechseln

Damit zu dem, was ich eigentlich wollte — und nicht bekam. Meine allgemeine CI-Flotte ist IPv6-only, ein kleiner IPv4-Pool existiert für die Jobs, die noch GitHub brauchen. Renovate läuft auf diesem IPv4-Pool. Der eigentliche Sinn der ganzen Übung war in meinem Kopf, es von dort herunterzuholen.

Es steht immer noch dort. Weil ein Renovate-Scan aus zwei völlig unabhängigen Gründen GitHub berührt — und ich nur einen davon behoben habe:

StatusGrundBeschreibung
✓ jetzt ohne GitHubTool-InstallationDas Herunterladen der php/composer-Binärdateien. Ein Hard-Fail-Pfad — ein Timeout hier färbt den gesamten Scan rot. Genau das habe ich gespiegelt.
→ weiterhin auf GitHubDatasource-LookupsDie Frage, welche Versionen GitHub-gehosteter Abhängigkeiten existieren. „Gibt es 8.5.9 schon?“ lässt sich nicht spiegeln, ohne einen echten Proxy vor die GitHub-API zu stellen — ein deutlich größeres Vorhaben.

Aus der Distanz sehen der Download-Pfad und der Metadaten-Pfad gleich aus — beide sagen „Renovate braucht GitHub“ — aber es sind nicht dieselbe Art von Problem. Das eine ist eine Datei, die man einmal kopiert und für immer ausliefert. Das andere ist eine Frage, die man GitHub jedes Mal neu stellen muss, und eine Frage zu spiegeln bedeutet, einen Proxy zu betreiben, der sie beantwortet — was wiederum bedeutet, GitHubs API-Oberfläche nachzubauen. Das steht diese Woche nicht an.

Lehre zwei

Einen von mehreren Gründen zu entfernen, warum ein Job das Netzwerk braucht, ist nicht dasselbe, wie den Job vom Netzwerk unabhängig zu machen. Erst die Gründe zählen, bevor man den Pool-Wechsel verspricht.

Also: ehrlich gesagt ein halber Sieg. Die instabile, scan-rot-färbende Abhängigkeit — der Download — ist weg, in Produktion verifiziert. Genau die war es auch wert, gekillt zu werden; sie war es, die aus fremden Ausfällen meine roten Pipelines machte. Renovate bleibt vorerst auf dem IPv4-Pool, und das Dokument, das mitschreibt, was noch IPv4 braucht, behält seine Renovate-Zeile — eine Zeile durchgestrichen, eine steht noch.

Ich nehme das so mit. Ein halber Sieg, der das tatsächliche Problem löst, schlägt einen ganzen Sieg, den man sich nur ausgedacht hat, um sich vollständig zu fühlen.

Häufige Fragen

Warum lässt sich der containerbase-Ansatz nicht einfach wie beim OpenTofu-Provider-Mirror lösen?+

Weil containerbase kein OCI spricht. Der OpenTofu-Provider-Mirror funktioniert, weil sowohl die Container-Registry als auch OpenTofu selbst das OCI-Protokoll verstehen. containerbase kennt nur einen simplen GET plus einen austauschbaren URL-Präfix — es braucht einen schlichten Dateiserver, keine Registry im engeren Sinn. GitLabs generische Package-Registry liefert genau das, zufällig mit einer URL-Form, die fast eins zu eins auf containerbases Erwartung passt.

Wie findet man heraus, welche Ubuntu-Variante und Architektur containerbase für ein bestimmtes Tool erwartet?+

Am zuverlässigsten empirisch, nicht durch Nachlesen in der Doku: URL_REPLACE testweise auf einen garantiert nicht auflösbaren Host zeigen (zum Beispiel probe.invalid) und den resultierenden DNS-Fehler lesen. Der enthält die vollständige, von containerbase zusammengebaute URL — inklusive Ubuntu-Codename und Architektur — und erspart das Rätselraten.

Warum reicht es nicht, einfach die neueste php- und composer-Version zu spiegeln?+

Weil Renovate mit binarySource=install pro Repository installiert, in der Version, die das jeweilige Repo tatsächlich verlangt — nicht in einer zentral vorgegebenen. Ohne vorherige Analyse der composer.json-Dateien aller verwalteten Repos ist unklar, wie viele unterschiedliche Versionen wirklich im Umlauf sind. Erst das Auszählen zeigte, dass es in diesem Fall nur drei waren.

Lässt sich diese Bestandsaufnahme automatisieren, statt composer.json von Hand zu lesen?+

Ja, im Kern ist es ein Skript, das über alle Repos iteriert, config.platform.php und require.php aus jeder composer.json extrahiert und gegen die aktuell neueste php-Version aus Packagist auflöst. Von Hand gelesen habe ich es hier, weil 73 Repos noch überschaubar sind — bei einer größeren Flotte würde ich diesen Schritt fest in die Mirror-Pipeline einbauen, damit neue exakte Pins automatisch auffallen.

Was passiert, wenn ein Repo künftig eine vierte, bisher nicht gespiegelte php-Version pinnt?+

Dann bekommt genau dieser Scan beim Tool-Install einen 404 — sichtbar und sofort diagnostizierbar, kein stiller Fallback auf GitHub. Die Behebung ist mechanisch: die neue Version zur Mirror-Liste hinzufügen, den Mirror-Job laufen lassen, den Scan erneut anstoißen. Genau dieses Verhalten war bei den OpenTofu-Providern bereits bewusst so gewählt.

Warum lohnt sich ein Proxy vor die GitHub-API für die Datasource-Lookups nicht, obwohl er das IPv4-Problem vollständig lösen würde?+

Weil er ein anderes, deutlich größeres Betriebsproblem aufmacht: Er müsste laufend aktuelle Versionsinformationen für beliebige GitHub-Repositories beantworten, nicht nur für eine feste, bekannte Liste von Dateien. Das ist eher der Bau einer eigenen GitHub-API-Fassade als ein Mirror — ein Vorhaben mit eigenem Wartungs- und Aktualitätsaufwand, der den Nutzen für diesen einen verbleibenden IPv4-Job aktuell nicht rechtfertigt.

Fazit

Zwei Geschichten mit derselben Ausgangsdiagnose, zwei sehr unterschiedliche Enden. Bei den Providern kontrolliere ich das Versions-Set, also war Spiegeln ein reines Rohrleitungsproblem. Bei Renovates Toolchain bestimmen 73 fremde Repos das Versions-Set, und containerbase spricht ohnehin kein OCI — beides zusammen macht aus derselben Idee eine andere Aufgabe. Der ehrliche Ertrag: eine instabile, scan-rot-färbende GitHub-Abhängigkeit ist weg, verifiziert in Produktion. Eine zweite bleibt bestehen, weil ihre Behebung eine eigene, deutlich größere Investition wäre. Beide Feststellungen zusammen sind wertvoller als eine geschönte Erfolgsgeschichte.

Ich analysiere, welche GitHub-Abhängigkeiten in Ihrer CI-Pipeline sich wirklich günstig spiegeln lassen — und sage ehrlich, welche das (noch) nicht tun.

Versions-Set-Analysen über composer.json/package.json hinweg, containerbase- und Provider-Mirroring, und die Unterscheidung zwischen Datei-Downloads und API-Lookups, die den Unterschied zwischen einem ganzen und einem halben Sieg ausmacht.

Plattform-Betrieb statt Beratung auf Papier: Ich prüfe, baue und pflege Ihre Supply-Chain-Mirrors laufend — inklusive der Fälle, in denen sich ein Mirror nicht lohnt.

Termin buchen →

Über den Autor

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.