In this lesson, you will learn how to drastically reduce your GitHub Actions build times by effectively caching your target/ directory and dependency registry.
When running CI for complex Rust workspaces, re-compiling every dependency from scratch on every push is a massive bottleneck. Because Cargo's compilation artifacts are stored in the target/ directory, we can persist this directory across CI runs. However, because target/ is massive and contains volatile build artifacts, a naive cache implementation often causes "cache bloat" or corruption.
The Strategy
To cache effectively, we target two distinct areas: the Cargo Registry (the downloaded source code) and the Cargo Target (the compiled binaries). In GitHub Actions, we use actions/cache to key these directories based on the Cargo.lock file.
- name: Cache cargo registry and target
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
Advanced Considerations
- Cache Invalidation: Using
hashFiles('**/Cargo.lock')as part of your key is vital. It ensures that whenever your dependency tree changes, you get a fresh cache rather than trying to build against stale, incompatible artifacts. - The
target/Trap: While cachingtarget/is powerful, remember that it contains build artifacts specific to the OS and compiler version. If you run builds on bothubuntu-latestandmacos-latest, always include${{ runner.os }}in your cache key prefix to prevent cross-platform file collisions. - Cleaning the Cache: Since GitHub Actions has a hard limit on total cache size (usually 10GB), keep an eye on your cache usage. If your workspace is gargantuan, consider caching only
~/.cargo/registryand~/.cargo/gitto speed up dependency downloading, while treating thetarget/folder as ephemeral to avoid reaching the cache size limit and triggering cache eviction thrashing.
By balancing the persistence of your registry and your local build artifacts, you ensure that your CI pipeline only does the "delta" work of compiling your actual application code, rather than the heavy lifting of your entire dependency tree.
Key Takeaway: Use a cache key derived from Cargo.lock to persist your target/ directory and registry, ensuring consistent, fast builds while avoiding cross-platform corruption.