GitHub Actions Caching: Pipelines 3x Sneller Zonder Extra Kosten

Dependency caching, Docker layer caching en custom cache strategies kunnen GitHub Actions pipelines drastisch versnellen en runner-minuten besparen.

Jean-Pierre Broeders

Freelance DevOps Engineer

4 april 20266 min. leestijd
GitHub Actions Caching: Pipelines 3x Sneller Zonder Extra Kosten

GitHub Actions Caching: Pipelines 3x Sneller

Een typische CI/CD pipeline installeert elke keer dezelfde dependencies, bouwt dezelfde Docker layers, en downloadt dezelfde tools. Dat kost tijd. Soms veel tijd. Een Node.js project met 500 packages? Makkelijk 2-3 minuten alleen voor npm install. Bij elke push.

Caching lost dit op. GitHub Actions heeft ingebouwde cache-mogelijkheden die pipelines kunnen versnellen met factor 3 of meer. Maar het werkt alleen als het goed geïmplementeerd wordt.

Dependency Caching: De Basisstrategie

De meest voorkomende use case: dependencies cachen. Zodra package.json niet verandert, hoeft node_modules/ niet opnieuw geïnstalleerd.

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

De key bepaalt wanneer de cache opnieuw wordt opgebouwd. Zolang package-lock.json hetzelfde blijft, wordt de cache gebruikt. Bij een dependency-update wordt de cache invalidated en opnieuw gebouwd.

De restore-keys werken als fallback. Als er geen exacte match is, gebruikt GitHub de meest recente cache die past bij het prefix. Dat betekent dat een pipeline nooit van nul begint, zelfs als dependencies veranderen.

Voor Python werkt het hetzelfde:

- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

Voor .NET projecten kan NuGet packages gecached worden:

- uses: actions/cache@v4
  with:
    path: ~/.nuget/packages
    key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
    restore-keys: |
      ${{ runner.os }}-nuget-

Docker Layer Caching: Build Snelheid Verdubbelen

Docker builds zijn traag. Elke layer wordt opnieuw gebouwd, zelfs als de code niet veranderd is. Layer caching helpt, maar standaard werkt het niet in GitHub Actions omdat elke job op een verse runner draait.

De oplossing: Docker Buildx met cache export/import.

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

- name: Cache Docker layers
  uses: actions/cache@v4
  with:
    path: /tmp/.buildx-cache
    key: ${{ runner.os }}-buildx-${{ github.sha }}
    restore-keys: |
      ${{ runner.os }}-buildx-

- name: Build Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: false
    tags: myapp:latest
    cache-from: type=local,src=/tmp/.buildx-cache
    cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

- name: Move cache
  run: |
    rm -rf /tmp/.buildx-cache
    mv /tmp/.buildx-cache-new /tmp/.buildx-cache

De cache-to wordt naar een nieuwe directory geschreven om race conditions te voorkomen. Zonder de mv aan het eind kan de cache corrupt raken.

Het verschil is merkbaar. Een typische Next.js app die 8 minuten nodig heeft voor een volledige build, doet het in 2-3 minuten met layer caching.

Custom Cache voor Build Artifacts

Dependencies zijn niet het enige wat gecached kan worden. Build artifacts, generated files, en test fixtures kunnen ook in de cache.

Een Webpack build die niet verandert hoeft niet opnieuw gedraaid:

- uses: actions/cache@v4
  with:
    path: .next/cache
    key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
    restore-keys: |
      ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
      ${{ runner.os }}-nextjs-

Voor een Gatsby site kan de .cache directory gecached:

- uses: actions/cache@v4
  with:
    path: |
      .cache
      public
    key: ${{ runner.os }}-gatsby-${{ hashFiles('gatsby-*.js', 'package-lock.json') }}

Multi-path Caching: Meerdere Directories in Eén Cache

Soms moeten meerdere directories gecached worden. Een TypeScript project met node_modules/ en build output in dist/:

- uses: actions/cache@v4
  with:
    path: |
      node_modules
      dist
    key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json', '**/tsconfig.json') }}

De pipe (|) syntax laat toe om meerdere paths op te geven. De cache wordt alleen opnieuw opgebouwd als een van de hash inputs verandert.

Cache Grootte Limitaties en Best Practices

GitHub beperkt de totale cache per repository tot 10 GB. Oudere caches worden automatisch verwijderd als de limiet bereikt wordt.

Dat betekent:

  • Hou caches klein. Cache niet node_modules/ maar ~/.npm.
  • Vermijd caching van grote binaries die snel veranderen.
  • Gebruik specifieke cache keys om te voorkomen dat oude caches blijven hangen.
Cache TypeTypische GrootteImpact op Snelheid
npm dependencies50-200 MB1-3 min bespaard
Docker layers500 MB - 2 GB3-6 min bespaard
Build artifacts10-100 MB30s - 2 min bespaard
pip/NuGet packages100-500 MB1-4 min bespaard

Cache Invalidatie: Wanneer Moet de Cache Opnieuw Opgebouwd?

Een cache key moet precies genoeg zijn om wijzigingen te detecteren, maar niet zo precies dat het elke keer opnieuw opgebouwd wordt.

Te breed:

key: ${{ runner.os }}-deps

Dit invalideert nooit. Dependencies veranderen, maar de cache blijft hetzelfde. Resulteert in oude packages.

Te specifiek:

key: ${{ runner.os }}-deps-${{ github.sha }}

Dit invalideert bij elke commit. De cache wordt nooit hergebruikt. Geen snelheidswinst.

Precies goed:

key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }}

Dit invalideert alleen als dependencies veranderen. Perfect.

Wanneer Caching NIET Helpt

Niet alles hoeft gecached. Sommige stappen zijn zo snel dat caching overhead toevoegt in plaats van verwijdert.

Skip caching voor:

  • Stappen die minder dan 10 seconden duren
  • Repositories met weinig dependencies
  • Jobs die een keer per dag draaien

Ook: als een project dependencies elke dag update, wordt de cache constant opnieuw opgebouwd. Dan is de winst minimaal.

Conclusie

Een goed gecachete GitHub Actions pipeline kan 50-70% van de totale runtime besparen. Dat betekent snellere feedback loops, minder wachttijd, en lagere kosten voor zowel publieke als private repositories.

Start met dependency caching. Voeg Docker layer caching toe als builds lang duren. Experimenteer met custom caches voor build artifacts. De impact is direct merkbaar.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op