Day 4 - Speed Up CI with Caching
Optimize a slow GitHub Actions workflow by adding intelligent caching for dependencies. Achieve 40% faster run times.
Description
Your GitHub Actions workflow runs every time, but it's downloading and installing the same dependencies repeatedly. Each run takes 5+ minutes when it could take less than 2 minutes with proper caching.
Task
Add caching for dependencies to your GitHub Actions workflow.
Goal: Achieve 40% faster run times through intelligent caching.
Target
- Time Reduction: 40% or more
- Cache Hit Rate: 80%+ on subsequent runs
- Cache Size: Reasonable (under 500MB)
Sample App
Slow Workflow (Before)
name: Slow CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
# No caching - downloads every time!
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Build
run: npm run build
View Solution
Solution
Optimized Workflow (After)
name: Fast CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm' # Built-in npm caching
- name: Cache node modules
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci # Faster than npm install
- name: Cache build output
uses: actions/cache@v3
with:
path: |
dist
.next
key: ${{ runner.os }}-build-${{ hashFiles('src/**') }}
restore-keys: |
${{ runner.os }}-build-
- name: Run tests
run: npm test
- name: Build
run: npm run build
Explanation
Cache Strategy
1. Package Manager Cache
- uses: actions/setup-node@v4
with:
cache: 'npm'
Benefits:
- Caches global npm cache directory
- Automatic invalidation on package-lock.json changes
- Zero configuration
2. Node Modules Cache
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Key Components:
- path: What to cache
- key: Unique identifier (OS + lockfile hash)
- restore-keys: Fallback if exact key doesn't match
3. Build Output Cache
- uses: actions/cache@v3
with:
path: dist
key: ${{ runner.os }}-build-${{ hashFiles('src/**') }}
Caches compiled output based on source code hash.
Cache Key Strategy
Exact Match Key
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Creates keys like:
Linux-node-a1b2c3d4e5f6...- Changes when dependencies change
Restore Keys (Fallbacks)
restore-keys: |
${{ runner.os }}-node-
If exact key not found:
- Try most recent cache starting with
Linux-node- - Partial cache better than no cache
Performance Comparison
| Scenario | Before | After | Improvement |
|---|---|---|---|
| Cold cache | 5m 30s | 5m 00s | 9% |
| Warm cache (no changes) | 5m 30s | 1m 45s | 68% |
| Dependency change | 5m 30s | 4m 30s | 18% |
Try to solve the challenge yourself first!
Click "Reveal Solution" when you're ready to see the answer.
Result
You should see:
- ✅ 40%+ faster average build times
- ✅ Cache hits on most runs
- ✅ Reduced GitHub Actions minutes usage
- ✅ Faster developer feedback
Workflow Log Output
Cache hit for key: Linux-node-a1b2c3d4e5f6
Restored cache from: /home/runner/.npm
Cache restored successfully
Total time: 1m 47s (previously 5m 32s)
Validation
Monitor Cache Performance
Add cache statistics step:
- name: Cache stats
run: |
echo "Cache key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
du -sh ~/.npm 2>/dev/null || echo "No cache"
du -sh node_modules 2>/dev/null || echo "No node_modules"
Compare Run Times
# View recent workflow runs
gh run list --workflow=ci.yml --limit 10
# Compare timing
gh run view <run-id> --log | grep "Total time"
Advanced Caching Patterns
Multi-Stage Caching
# Stage 1: Dependencies
- uses: actions/cache@v3
id: cache-deps
with:
path: node_modules
key: deps-${{ hashFiles('package-lock.json') }}
# Stage 2: Build (depends on source)
- uses: actions/cache@v3
id: cache-build
if: steps.cache-deps.outputs.cache-hit == 'true'
with:
path: dist
key: build-${{ hashFiles('src/**') }}
# Only build if cache miss
- name: Build
if: steps.cache-build.outputs.cache-hit != 'true'
run: npm run build
Language-Specific Caching
Python (pip)
- uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'
Go
- uses: actions/setup-go@v4
with:
go-version: '1.21'
cache: true
Docker Layers
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
Cache Cleanup
- name: Clear old caches
if: github.event_name == 'schedule'
run: |
gh cache delete --all --repo ${{ github.repository }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Best Practices
✅ Do's
- Use specific cache keys: Include lockfile hashes
- Set restore-keys: Fallback to partial matches
- Cache immutable data: node_modules, build artifacts
- Monitor cache size: Keep under 500MB when possible
- Version your caches: Include version in key if needed
❌ Don'ts
- Don't cache secrets: Never cache credentials
- Don't cache .git: Checkout action handles this
- Don't use generic keys:
cache-v1is too broad - Don't cache OS files: System-specific files
- Don't ignore cache misses: Monitor and optimize
Links
Share Your Success
Optimized your CI? Share the results!
Tag @thedevopsdaily on X with:
- Before/after workflow times
- Cache hit rate percentage
- Amount of time/money saved
Use hashtags: #AdventOfDevOps #GitHubActions #Performance #Day4
Ready to complete this challenge?
Mark this challenge as complete once you've finished the task. We'll track your progress!
Completed this challenge? Share your success!
Tag @thedevopsdaily on X (Twitter) and share your learning journey with the community!
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
Acronis
The most secure backup
Acronis: the most secure backup solution for your data
Pluralsight
Technology skills platform
Expert-led courses in software development, IT ops, data, and cybersecurity
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?