Caching Dependencies
Learn how to cache dependencies in GitHub Actions for faster builds
Caching Dependencies
Caching dependencies in GitHub Actions significantly reduces build times by storing and reusing downloaded packages and compiled artifacts between workflow runs.
Why Cache Dependencies?
Benefits
- Faster builds: Skip downloading dependencies
- Reduced bandwidth: Less network usage
- Lower costs: Fewer billable minutes
- More reliable: Less dependent on external services
What to Cache
- Package managers: npm, pip, Maven, Gradle
- Build artifacts: Compiled code, generated files
- Tools: Downloaded binaries, SDKs
- Data: Test fixtures, datasets
Basic Caching
Cache Action
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
Cache Keys
# Include OS in key
key: ${{ runner.os }}-deps-${{ hashFiles('package.json') }}
# Include multiple files
key: deps-${{ hashFiles('package.json', 'yarn.lock') }}
# Include branch name
key: ${{ runner.os }}-${{ github.ref }}-${{ hashFiles('**/pom.xml') }}
Language-Specific Caching
Node.js / npm
- name: Cache npm dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
Python / pip
- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: pip install -r requirements.txt
Java / Maven
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-
- name: Build with Maven
run: mvn clean compile
Ruby / Bundler
- name: Cache gem dependencies
uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
- name: Install dependencies
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
Advanced Caching Strategies
Multiple Cache Paths
- name: Cache multiple paths
uses: actions/cache@v3
with:
path: |
~/.npm
~/.cache
node_modules
key: ${{ runner.os }}-deps-${{ hashFiles('package-lock.json') }}
Conditional Caching
- name: Cache only on main branch
if: github.ref == 'refs/heads/main'
uses: actions/cache@v3
with:
path: ~/.npm
key: main-${{ hashFiles('package-lock.json') }}
Build Artifact Caching
- name: Cache build artifacts
uses: actions/cache@v3
with:
path: |
dist/
build/
key: build-${{ github.sha }}
restore-keys: |
build-${{ github.ref }}-
build-
Cache Management
Cache Limits
- Total cache size: 10GB per repository
- Individual cache: 5GB maximum
- Retention: 7 days if not accessed
Cache Cleanup
# Automatic cleanup when limits exceeded
# Least recently used caches removed first
# Manual cache management
- name: Clear cache (manual trigger)
if: github.event.inputs.clear_cache == 'true'
run: |
echo "Cache will be rebuilt"
Setup Actions with Caching
Node.js Setup with Cache
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm' # Built-in caching
Python Setup with Cache
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
cache: 'pip' # Built-in caching
Java Setup with Cache
- name: Setup Java
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'maven' # Built-in caching
Best Practices
Cache Key Strategy
# Good: Specific and unique
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
# Bad: Too generic
key: dependencies
# Good: Hierarchical restore keys
restore-keys: |
${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
${{ runner.os }}-node-
${{ runner.os }}-
Cache Validation
- name: Validate cache
run: |
if [ -d "node_modules" ]; then
echo "Cache hit: node_modules exists"
else
echo "Cache miss: installing dependencies"
fi
Cache Warming
# Separate job to warm cache
cache-warmup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Warm cache
uses: actions/cache@v3
with:
path: ~/.npm
key: warmup-${{ hashFiles('package-lock.json') }}
- run: npm ci
Troubleshooting
Cache Miss Issues
- name: Debug cache
run: |
echo "Cache key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
echo "Files used for hash:"
find . -name "package-lock.json" -type f
Cache Size Optimization
# Clean unnecessary files before caching
- name: Clean cache
run: |
npm prune
rm -rf node_modules/.cache
Complete Example
name: Build and Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Cache build artifacts
uses: actions/cache@v3
with:
path: |
dist/
.next/cache
key: build-${{ runner.os }}-${{ github.sha }}
restore-keys: |
build-${{ runner.os }}-
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Run tests
run: npm test