Optimizing CI/CD workflow times directly impacts developer productivity and reduces computing expenses. This article shows you how to integrate dependency caching in GitHub Actions, helping you slash compilation and package setup times.
We provide YAML workflow templates for Node.js, Python, and Rust, alongside best practices to ensure optimal cache hits.
1. Why Cache Dependencies in CI/CD?
When a CI agent starts without caching, it spins up a clean container and fetches every dependency from package registries. This introduces several drawbacks:
- Inefficient Dev Loops: Developers waste precious minutes waiting for standard libraries to install before testing code.
- Network Risks: If npm, PyPI, or crates.io undergoes minor downtime, your build fails due to external network issues.
- Execution Costs: On private repositories, GitHub charges you for every active minute. Faster builds directly reduce operations costs.
Caching allows the pipeline to reuse downloaded resources across executions, making subsequent runs execute up to 80% faster.
2. YAML Templates for Major Languages
In 2026, GitHub’s default language setup actions include built-in caching support, making configuration straightforward.
1) Node.js (npm / pnpm / yarn)
Configure the cache property under actions/setup-node:
name: Node.js CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Supports npm, yarn, or pnpm
- name: Install Packages
run: npm ci
- name: Run Tests
run: npm test
2) Python (pip / poetry)
For Python environments, enable caching using the cache key in actions/setup-python:
name: Python Test Runner
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip' # Supports pip, poetry, or pipenv
- name: Install Dependencies
run: pip install -r requirements.txt
- name: Run Linter
run: flake8 .
3) Rust (Cargo)
Rust compilation is notoriously slow. To cache Cargo dependencies and intermediate build artifacts (the /target directory), use the community standard swatinem/rust-cache:
name: Rust CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust compiler
uses: actions/setup-rust@v1
- name: Configure Cargo Cache
uses: swatinem/rust-cache@v2
- name: Execute Tests
run: cargo test
3. Custom Caching and Troubleshooting
For folders not handled by default actions, define them manually using actions/cache:
- name: Cache Custom Files
uses: actions/cache@v4
with:
path: ~/.my-custom-cache
key: ${{ runner.os }}-custom-${{ hashFiles('**/lockfile.json') }}
restore-keys: |
${{ runner.os }}-custom-
key: Unique identifier for the cache. ThehashFilesfunction creates a hash of your lockfiles (package-lock.json,Cargo.lock, etc.). When a lockfile changes, the old cache is invalidated and a fresh one is built.restore-keys: An ordered list of prefix keys to fall back on if an exact cache hit is not found.
Preventing Cache Misses
- Commit Lockfiles: Ensure lockfiles are tracked in Git. Without them, hashes fluctuate, causing frequent cache misses.
- Track Cache Size: GitHub limits cache storage to 10GB per repository. Once you exceed this limit, GitHub automatically evicts older caches. Exclude unnecessary build artifacts from your cache paths.
4. Summary
Configuring actions caching is one of the easiest ways to optimize your software pipelines. Implementing these changes can save hours of wait time across your team every week. Verify your YAML workflows and integrate caching today.
