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
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 Type | Typische Grootte | Impact op Snelheid |
|---|---|---|
| npm dependencies | 50-200 MB | 1-3 min bespaard |
| Docker layers | 500 MB - 2 GB | 3-6 min bespaard |
| Build artifacts | 10-100 MB | 30s - 2 min bespaard |
| pip/NuGet packages | 100-500 MB | 1-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.
