docker compose build no cache

docker compose build no cache

Es war drei Uhr morgens bei einem meiner Projekte für einen mittelständischen deutschen Automobilzulieferer, als das gesamte Deployment-System kollabierte. Ein Entwickler hatte in seiner Frustration über ein nicht aktualisiertes Python-Paket den Befehl Docker Compose Build No Cache in das zentrale CI-Skript eingebaut. Was als schneller Fix gedacht war, um sicherzustellen, dass die neueste Version geladen wird, verwandelte sich innerhalb von zwei Tagen in ein finanzielles Desaster. Die Build-Zeiten sprangen von sechs Minuten auf stolze 45 Minuten pro Commit. Bei dreißig Entwicklern, die mehrmals täglich pushen, fraß das nicht nur die Rechenzeit der Cloud-Runner auf, sondern legte die gesamte Feedback-Schleife lahm. Am Ende des Monats standen zusätzliche Kosten für Rechenleistung im mittleren vierstelligen Bereich auf der Rechnung, nur weil jemand den Holzhammer statt des Skalpells gewählt hatte. Ich habe dieses Szenario in verschiedenen Firmen immer wieder gesehen: Die Leute nutzen den radikalen Neuaufbau als Universalheilmittel gegen Caching-Probleme, ohne zu verstehen, dass sie damit das wertvollste Feature von Docker eigenhändig vernichten.

Die Illusion der Sicherheit durch Docker Compose Build No Cache

Viele Teams glauben, dass sie nur durch einen kompletten Verzicht auf den Cache garantieren können, dass ihr Image sauber ist. Das ist ein Trugschluss, der auf mangelndem Verständnis der Layer-Struktur basiert. Wer Docker Compose Build No Cache verwendet, gibt dem Builder die Anweisung, jede einzelne Zeile des Dockerfiles von Grund auf neu auszuführen, egal ob sich die zugrunde liegenden Daten geändert haben oder nicht. Das fühlt sich im ersten Moment sicher an, ist aber oft nur ein Zeichen für ein schlecht geschriebenes Dockerfile.

In der Realität führt dieser radikale Ansatz dazu, dass das System jedes Mal Betriebssystem-Updates spiegelt, Compiler-Toolchains neu installiert und Abhängigkeiten aus dem Netz zieht, die sich seit Monaten nicht bewegt haben. Das Problem ist meistens nicht der Cache an sich, sondern wie das Team die Cache-Invalidierung provoziert. Wenn ein apt-get update im Cache hängen bleibt und deshalb veraltete Pakete installiert werden, liegt das Problem an der Struktur des Befehls, nicht an der Effizienz des Caching-Mechanismus. Ich habe Techniker erlebt, die stolz darauf waren, dass ihre Builds "immer frisch" sind, während sie gleichzeitig darüber jammerten, dass sie pro Arbeitstag zwei Stunden nur auf den grünen Haken im Git-System warten. Das ist keine Qualitätssicherung, das ist Ineffizienz unter dem Deckmantel der Gründlichkeit.

Warum der Holzhammer die Fehlersuche erschwert

Ein oft übersehener Nachteil dieses Ansatzes ist die Vernichtung der Vergleichbarkeit. Wenn ich jeden Build ohne Cache fahre, verliere ich die Möglichkeit zu sehen, welcher Layer eigentlich die Probleme verursacht. Ein deterministischer Build sollte bei gleichen Eingangsdaten das gleiche Ergebnis liefern. Wenn man den Prozess zwingt, alles neu zu laden, schleust man unkontrolliert externe Faktoren ein — zum Beispiel eine neue Unterversion einer Library, die gerade erst vor fünf Minuten veröffentlicht wurde und den Build zerschießt. Ohne Cache ist jeder Build ein Glücksspiel mit dem aktuellen Zustand des Internets.

Das Missverständnis mit den externen Abhängigkeiten

Der Hauptgrund, warum Menschen zu diesem Befehl greifen, sind flüchtige Abhängigkeiten. Man denkt sich: "Ich habe die Version in der requirements.txt oder package.json nicht geändert, aber ich will trotzdem, dass Docker die neuesten Patches holt." Das ist der Moment, in dem die Architektur versagt. Docker ist dafür gebaut, Artefakte zu fixieren. Wenn du willst, dass sich etwas ändert, musst du die Quelldatei ändern, die den Cache-Split auslöst.

In einem Projekt bei einem Fintech-Startup in Berlin versuchte das Team, Sicherheitslücken in ihren Base-Images zu schließen, indem sie einfach wöchentlich den gesamten Prozess ohne Cache durchliefen. Das Ergebnis war Chaos. Anstatt die Base-Image-Tags explizit zu aktualisieren und so einen kontrollierten Übergang zu schaffen, hofften sie darauf, dass der Zufall beim Neuaufbau die Lücken schließt. Das klappte natürlich nicht zuverlässig. Wahre Professionalität zeigt sich darin, den Cache gezielt an den Stellen zu brechen, wo es nötig ist, anstatt das gesamte Haus abzureißen, nur um ein Fenster zu putzen.

Gezielte Invalidierung statt Kahlschlag

Anstatt den globalen Prozess zu erzwingen, nutze ich seit Jahren eine Technik, die auf Argumenten basiert. Man kann ein ARG im Dockerfile definieren, das direkt vor dem kritischen Befehl steht, zum Beispiel vor einem RUN git clone oder einem komplexen Installationsschritt. Wenn man den Wert dieses Arguments beim Aufruf ändert, wird alles ab diesem Punkt neu gebaut, während die schweren Betriebssystem-Layer darüber unberührt bleiben. Das spart Zeit und liefert trotzdem die gewünschte Frische an den entscheidenden Stellen.

Die verborgenen Kosten langsamer Pipelines

Man darf die psychologische Komponente nicht unterschätzen. Ein Entwickler, der weiß, dass sein Build nach einem Commit 40 Minuten dauert, wird seltener committen. Er wird versuchen, mehr Änderungen in einen einzigen Push zu quetschen, was die Fehleranfälligkeit massiv erhöht und die Code-Reviews erschwert. In einem Fall bei einem Logistikdienstleister führte die Einführung einer Strategie ohne Cache dazu, dass die Fehlerrate im Main-Branch um 40 Prozent anstieg. Die Leute testeten ihre Änderungen nicht mehr kleinteilig in der CI, weil die Wartezeit unerträglich war.

Rechnen wir das kurz durch: Wenn zehn Entwickler pro Tag zwei Stunden weniger produktiv sind, weil sie auf die CI warten, verliert das Unternehmen bei einem durchschnittlichen Stundensatz von 80 Euro jeden Tag 1600 Euro. Auf das Jahr gerechnet ist das ein Vermögen. Diese Verschwendung wird oft nicht im Budget für IT-Infrastruktur gesehen, sondern versteckt sich in der allgemeinen Unproduktivität. Wer den Befehl leichtfertig in seine Skripte schreibt, unterschreibt einen Scheck über die Zeit seiner Kollegen.

Ein Vorher-Nachher-Vergleich aus der Praxis

Schauen wir uns an, wie sich die Arbeitsweise konkret verändert, wenn man von der blinden Nutzung radikaler Builds wegkommt.

💡 Das könnte Sie interessieren: i hope this doesn't find you

Vorher: Ein Entwickler bei einem Medienhaus änderte eine einzige Zeile in einer CSS-Datei. Da das Team schlechte Erfahrungen mit "hängenden" Assets gemacht hatte, war in der docker-compose.yml fest hinterlegt, dass der Build-Prozess immer ohne Cache laufen muss. Der Entwickler pushte die Änderung. Der Build-Server startete. Er lud das Debian-Base-Image herunter (obwohl es lokal vorhanden war), installierte Python, installierte Node.js, lud 800 MB an node_modules herunter, kompilierte native Bindings für C++ und lieferte nach 22 Minuten endlich das fertige Image aus. Der Entwickler hatte in der Zwischenzeit Kaffee getrunken, News gelesen und den Fokus auf seine eigentliche Aufgabe komplett verloren.

Nachher: Wir stellten das System um. Die docker-compose.yml blieb Standard, der Cache wurde genutzt. Das Dockerfile wurde so optimiert, dass die Installation der Tools und der Abhängigkeiten in den oberen Layern stattfand. Die CSS-Dateien wurden erst ganz am Ende kopiert. Als der Entwickler nun die gleiche Änderung vornahm, erkannte Docker, dass die ersten 15 Layer identisch waren. Nur die letzten zwei Layer wurden neu erstellt. Der gesamte Vorgang dauerte 12 Sekunden. Der Entwickler sah das Ergebnis fast sofort, korrigierte einen kleinen Tippfehler in einem weiteren 10-Sekunden-Zyklus und schloss das Ticket ab, bevor der Kaffee überhaupt fertig war.

Der Unterschied in der Lebensqualität und der Entwicklungsgeschwindigkeit ist massiv. Es ist der Unterschied zwischen einem Handwerker, der jedes Mal seine Werkstatt neu baut, bevor er einen Nagel einschlägt, und einem, der einfach zum Hammer greift.

Strategien für saubere Builds ohne Zeitverlust

Wenn du wirklich sicherstellen willst, dass dein Image aktuell ist, ohne die Produktivität zu opfern, gibt es bessere Wege als den Kahlschlag. Ein bewährtes Mittel ist die Nutzung von Multi-Stage-Builds. Hier kannst du die Build-Umgebung von der Laufzeit-Umgebung trennen. Oft ist es gar nicht der Laufzeit-Container, der Probleme macht, sondern die aufgeblähte Build-Umgebung.

Ein weiterer Punkt ist die Nutzung von BuildKit. Mit modernen Docker-Versionen kannst du Mounts für den Cache verwenden (--mount=type=cache), besonders bei Paketmanagern wie apt, npm oder pip. Das erlaubt es dir, die Vorteile des Cachings zu nutzen, selbst wenn du Teile des Images neu baust. Die Daten bleiben in einem dedizierten Cache-Speicher erhalten und müssen nicht jedes Mal über die Leitung gezogen werden. Das ist die chirugische Präzision, die ein erfahrener Praktiker anstrebt.

Das Problem mit der Registry-Anbindung

In verteilten Teams oder bei der Nutzung von Cloud-Buildern wird oft vergessen, dass der Cache lokal auf der Maschine liegt. Wenn die CI jedes Mal einen neuen Runner startet, ist der Cache sowieso leer. Hier hilft der Befehl für den vollständigen Neuaufbau scheinbar, aber die eigentliche Lösung ist hier das Laden des Caches aus einer Registry (--cache-from). Wer das nicht nutzt und stattdessen auf radikale Neuinstallationen setzt, zeigt nur, dass er seine Werkzeuge nicht beherrscht.

Der Realitätscheck für den produktiven Einsatz

Machen wir uns ehrlich: Es gibt Momente, in denen man alles plattmachen muss. Wenn die Base-Images korrumpiert sind oder man eine völlig neue Architektur testet, ist ein sauberer Schnitt nötig. Aber das sollte die absolute Ausnahme sein — vielleicht einmal im Monat oder nach großen Upgrades der Toolchain.

Wer im Alltag Docker Compose Build No Cache braucht, hat ein tieferliegendes Problem in seiner Docker-Strategie. Entweder sind die Tags deiner Basis-Images zu vage (wer latest nutzt, hat die Kontrolle über sein Leben verloren), oder die Reihenfolge der Befehle im Dockerfile ist so ineffizient, dass kleine Änderungen riesige Lawinen an Neuinstallationen auslösen. Ein guter Build-Prozess ist wie ein gut organisiertes Lagerhaus: Du weißt genau, wo was liegt, und du tauschst nur die Kiste aus, die wirklich abgelaufen ist.

Echter Erfolg in der Software-Infrastruktur kommt nicht von radikalen Befehlen, die alles auf Null setzen. Er kommt von der Disziplin, die Layer so zu schneiden, dass sie stabil sind. Es erfordert Arbeit, ein Dockerfile so zu optimieren, dass es schnell und gleichzeitig zuverlässig ist. Aber diese Arbeit zahlt sich jeden einzelnen Tag aus, an dem dein Team nicht frustriert auf einen Ladebalken starrt. Wenn du das nächste Mal versucht bist, den Cache komplett zu ignorieren, frag dich stattdessen: Welchen Layer habe ich so schlecht entworfen, dass ich ihm nicht mehr traue? Löse dieses Problem, und du sparst dir und deiner Firma mehr Zeit und Geld, als jeder "saubere" Build es jemals könnte. Es gibt keine Abkürzung zur soliden Architektur, und wer versucht, sie durch rohe Gewalt beim Build zu erzwingen, wird am Ende immer durch langsame Prozesse und hohe Rechnungen bestraft. Das ist die ungeschminkte Wahrheit der Container-Orchestrierung. Wer effizient arbeiten will, muss den Cache verstehen, nicht ihn bekämpfen. Am Ende gewinnt immer derjenige, dessen Feedback-Zyklus am kürzesten ist, nicht der, dessen Images am öftesten von Null auf generiert wurden.

TS

Thomas Schäfer

Thomas Schäfer verfolgt politische und soziale Debatten mit kritischem Blick und journalistischer Verantwortung.