Skip to main content
Caching Dependencies on GitHub Actions
4 min read

Caching Dependencies on GitHub Actions

Introduction

GitHub Actions offer two ways to store files: caching (for things like dependencies) and artifacts (for job results like logs or binaries). While they sound similar, they have different purposes, so we should use caching to speed up workflow runs.

Another important thing is that cache access is limited to specific branches: the current branch, the base branch for pull requests, and the default branch. So, caches created in unrelated branches won’t be accessible, but that’s not something that’ll affect us in most cases, as we usually stick to the current or base branches.

By using caching correctly, we can cut down build times, especially for projects with stable dependencies.

Choosing the Right Cache Key

We can generate cache keys based on metadata, like OS or commit hashes, to reuse dependencies only when necessary. Additionally, we can use restore keys to find close matches for caches, which helps minimize rebuild times.

For workflows running on multiple OS environments, it’s also a good idea to isolate caches per OS to avoid unnecessary rebuilds across platforms. In some cases, we might even use temporary caches that are only valid for a single run, which is useful when we don’t need long-term reuse.


- uses: actions/cache@v4
with:
path: path/to/dependencies
# generate a cache key based on the hash of lockfiles
key: cache-${{ hashFiles('**/lockfiles') }}

- uses: actions/cache@v4
with:
path: path/to/dependencies
key: cache-${{ hashFiles('**/lockfiles') }}
# restore keys for closest matches. This minimizes the time spent downloading newer dependencies
restore-keys: |
cache-npm-

- uses: actions/cache@v4
with:
path: path/to/dependencies
#key: cache-${{ hashFiles('**/lockfiles') }}
# OS-specific caches to avoid unnecessary rebuilds across different platforms
key: ${{ runner.os }}-cache
restore-keys: |
cache-npm-


- uses: actions/cache@v4
with:
path: path/to/dependencies
#key: cache-${{ hashFiles('**/lockfiles') }}
#key: ${{ runner.os }}-cache
# short-lived caches for one-time use
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
cache-npm-

We can also share caches across jobs, centralizing cache creation to save time, and ensure caches are saved even if a build fails by using the always() condition.

# Centralized cache reuse

- id: cache-primes-save
uses: actions/cache/save@v4 # Save cache
with:
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}

- uses: actions/cache/restore@v4 # Restore cache
with:
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}

# Save cache after build failure
- uses: actions/cache@v4
with:
path: path/to/dependencies
key: cache-${{ hashFiles('**/lockfiles') }}
if: always()

GitHub Actions Package Manager Caching Examples

I’ve put together some caching examples for the package managers we use (npm, pip, Maven, and NuGet). These configurations should help us avoid redownloading dependencies in every workflow run.

For instance, with npm, we avoid caching node_modules to prevent issues with different Node versions. Instead, we dynamically cache npm itself. Similarly, for pip, we specify cache paths based on the OS, and we do the same for Maven and NuGet using their respective repository paths and lock files.

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

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

# Cache Maven dependencies
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}

# Cache NuGet dependencies
- uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: ${{ runner.os }}-nuget-

Conclusion

Caching in GitHub Actions includes how to optimize the continuous integration workflow by basically keeping away from doing duplicate tasks, say, re-downloading of dependencies. Carefully selecting metadata, operating system, or lock file-based cache keys, finding close matches with restore keys, and keeping the number less will highly reduce build times. Also, keep separate caches for different operating systems and use transient caches for one-time builds.

Monitoring GitHub Actions Workflows

CICube is a GitHub Actions monitoring tool that provides you with detailed insights into your workflows to further optimize your CI/CD pipeline. With CICube, you will be able to track your workflow runs, understand where the bottlenecks are, and tease out the best from your build times. Go to cicube.io now and create a free account to better optimize your GitHub Actions workflows!

CICube GitHub Actions Workflow Duration Monitoring