GitHub Actions schneller machen – Abhängigkeiten und Docker-Layer cachen

Kaltes CI ist langsam: Jede Pipeline lädt Abhängigkeiten, kompiliert immer wieder und baut Docker Images von Grund auf. Mit Caching vermeidest du doppelte Arbeit. Das spart Laufzeit, Kosten und macht deine Builds stabiler.

In diesem Guide lernst du, wie GitHub Actions Caching funktioniert, wie du Keys klug wählst, was der Unterschied zu Artifacts ist und wie du für Node.js, Python, Java und Docker praxistaugliche Caches einrichtest. Schritt für Schritt, ohne Vorwissen, mit kopierbaren Snippets.

Grundlagen: wie der Cache in GitHub Actions arbeitet

Ein Cache speichert Dateien zwischen Workflow-Läufen. Du definierst path (was cachen), key (wann wiederverwenden) und optional restore-keys (Fallbacks). Stimmt der key, gibt es einen Cache-Hit und der Job spart Minuten.

Wichtige Punkte:

  • Cache ist nicht gleich Artifact. Artifacts sind Build-Ergebnisse zum Download, Cache ist nur für schnellere Builds gedacht.
  • Nutze Lockfiles (package-lock.json, poetry.lock, pom.xml, gradle.lockfile) für stabile Keys.
  • Caches sind projektweit nutzbar, können aber per restore-keys auch branchenübergreifend gefunden werden.

Keys und Restore-Keys: die Strategie

Ein guter key repräsentiert den Zustand der Abhängigkeiten. Beispiel: npm-${{ hashFiles('**/package-lock.json') }}. Ändert sich das Lockfile, gibt es Miss, sonst Hit.
restore-keys sind Präfixe, z. B. npm-, um einen nahen Cache zu finden, wenn der exakte Key fehlt. Das ist nützlich bei Branches und Monorepos.


Node.js: npm oder pnpm sauber cachen

schnelles Setup mit setup-node

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
          cache-dependency-path: 'package-lock.json'
      - run: npm ci
      - run: npm test

Vorteile: Automatischer Cache der npm-Daten. Der Key kommt aus dem Lockfile.

individueller Cache mit actions/cache

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-

Hinweis: Cache npm-Store statt node_modules. Mit npm ci bekommst du reproduzierbare Builds.


Python: pip, pip-tools, Poetry

pip Cache

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'
    cache-dependency-path: 'requirements.txt'
- run: pip install -r requirements.txt

Poetry Cache

- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/pypoetry
      ~/.cache/pip
    key: poetry-${{ hashFiles('**/poetry.lock') }}
    restore-keys: |
      poetry-
- run: poetry install --no-interaction --no-ansi

Tipp: Lockfile zwingend nutzen, damit der Cache deterministisch ist.


Java: Maven und Gradle

Maven

- uses: actions/cache@v4
  with:
    path: ~/.m2/repository
    key: maven-${{ hashFiles('**/pom.xml') }}
    restore-keys: |
      maven-
- run: mvn -B -ntp verify

Gradle

- uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: |
      gradle-
- run: ./gradlew build --no-daemon

Best Practice: Wrapper verwenden und Gradle Daemon im CI deaktivieren, um saubere Logs zu bekommen.


Docker: Layer Caching mit Buildx

Buildx und GHA Cache

- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
  with:
    context: .
    file: ./Dockerfile
    push: false
    tags: myapp:ci
    cache-from: type=gha
    cache-to: type=gha,mode=max

Die Optionen cache-from/cache-to nutzen den GitHub Actions Cache für Image-Layer. Achte auf eine schlanke Dockerfile-Struktur: abhängigkeitslastige Schritte früh cachen, Quellcode spät kopieren.

Dockerfile Tipp:

  • trenne Dependencies und App-Code
  • nutze multi-stage builds
  • pinne Basisimages für reproduzierbare Layer

Monorepo und Matrix: Caches pro Paket

In Monorepos brauchst du paketspezifische Keys, z. B. npm-appA-${{ hashFiles('apps/appA/package-lock.json') }}. In Matrix-Builds generierst du Keys aus dem Matrix-Namen. So vermeiden sich Jobs gegenseitig zu überschreiben.


Troubleshooting: Cache trifft nicht – was nun

Wenn der Cache nicht greift:

  • Prüfe, ob key sich ständig ändert (Zeitstempel vermeiden).
  • Stelle sicher, dass die paths korrekt sind und das Tool diese Verzeichnisse nutzt.
  • Nutze restore-keys, um nahe Treffer zu erlauben.
  • Leere bei Problemen gezielt den Cache, indem du den Key absichtlich änderst (z. B. ein Suffix).

Sicherheit: Lege keine Secrets in Cache-Verzeichnisse. Caches sind für Build-Beschleunigung, nicht für sensible Daten.


Best Practices auf einen Blick

Setze Lockfiles als Key-Basis, cache die Package-Manager-Stores statt der Build-Outputs, halte Keys kurz und stabil, nutze restore-keys für Branch-Fallback, designe Dockerfiles für hohe Layer-Wiederverwendung und dokumentiere deine Cache-Pfade direkt im README des Repos.

Fazit

Mit sauber gesetzten Keys, restore-keys und den richtigen Cache-Pfaden reduzierst du die CI-Laufzeit drastisch. Node, Python, Java und Docker profitieren sofort, wenn du Stores und Layer cachen lässt. Das Ergebnis: schnellere Builds, weniger Kosten, glücklichere Teams.

0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert