Caching Cargo Builds in CI With Swatinem Rust Cache
Cache the Cargo registry and target directory in CI with Swatinem/rust-cache. Learn what is cached, the cache key, why caches go stale and how to keep restore times low.

A cold Rust CI run compiles every dependency. A warm one compiles only your code. The difference is minutes against seconds, and one action provides it. The tuning is mostly about not defeating it: different flags in different jobs, caches that grow until the restore takes longer than the build, and pull requests that cannot see the main branch cache. This post is the caching step of GitHub Actions for Rust.
GitHub Actions, Swatinem/rust-cache v2, Rust 1.90.
The step
- uses: dtolnay/[email protected]
- uses: Swatinem/rust-cache@v2
with:
shared-key: "ci"
cache-on-failure: trueIt must run after the toolchain step, because the key includes the compiler version. shared-key lets several jobs (test, clippy, doc) use one cache instead of building the same dependencies three times. cache-on-failure saves the cache even when tests fail, so a failing build does not lose the work.
What it saves
On the post step it stores:
~/.cargo/registryand git checkouts (downloaded crates)target/with the artifacts of dependencies- the version of
rustcand hashes ofCargo.lockandCargo.tomlfiles in the key
Before saving, it deletes the artifacts of workspace members and other fast-changing files. Your own crates would never hit anyway, and including them is what makes naive actions/cache setups grow. Dependencies from git or paths are treated as workspace code when they change often.
Where caches go wrong
| Symptom | Cause | Fix |
|---|---|---|
| Never restores | key changes each run | check RUSTFLAGS and toolchain version are stable |
| Restores, still rebuilds all | different features or flags | use the same flags in the jobs that share a cache |
| Restore takes 2 minutes | cache is huge | remove unused features, drop --all-features jobs from the shared key |
| PRs build cold | no cache on the base branch | run the workflow on pushes to main |
| Cache evicted often | repository over the storage limit | prune old keys, shrink the cache |
RUSTFLAGS deserves emphasis: -D warnings in one job and none in another yields artifacts that Cargo treats as different, because flags are part of the fingerprint.
Make builds cheaper regardless of cache
- Use
cargo build --lockedso the lock never changes during CI. - Set
CARGO_INCREMENTAL=0in CI; incremental data is large and does not help from a cold start. The action sets this by default. - Use a faster linker such as
lldormoldon Linux. - Trim dependencies and features; compile time scales with them.
sccache
sccache wraps rustc and caches each compilation by hash, in a local directory, S3 or GitHub's cache. Set RUSTC_WRAPPER=sccache. It helps when many jobs or machines build the same crates, and when you need the cache outside GitHub. It does not cache linking, incremental builds or crates with build scripts that vary. For a single repository on GitHub, the action above is simpler.
Docker builds
Caching inside docker build is a different mechanism: layer caching, covered with cargo-chef, and cache mounts.
Limits
A cache cannot help the first build after a dependency bump, which compiles that dependency and everything that depends on it. If compile time remains the bottleneck, look at what is being compiled with cargo build --timings. The action's README documents the options: Swatinem/rust-cache.


