Speed Up Rust Cargo in GitHub Actions
Last updated
To speed up Rust and Cargo in GitHub Actions, cache the target directory and the cargo registry with Swatinem/rust-cache, turn off incremental compilation with CARGO_INCREMENTAL=0, run tests with cargo-nextest, and give the build more cores. Rust CI is slow mainly because dependency compilation is heavy and repeated on every fresh VM.
This guide gives a working workflow and explains the trade-offs. For the general version, see how to speed up GitHub Actions.
Cache dependencies with rust-cache
Swatinem/rust-cache is the standard choice. Its README documents what it caches: ~/.cargo (registry, cache, git dependencies, installed binaries) and ./target (build artifacts of dependencies). Before saving, it removes things that do not help next time: incremental artifacts, workspace crates, unused dependencies and files older than a week. That keeps the cache small.
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
Inputs worth knowing, all from the README:
shared-key: use one key across jobs that build the same thing, such asbuildandtest, so they share a cache. Without it, the job id is part of the key.workspaces: map workspaces to target directories, for example. -> target. Needed for non-standard layouts.save-if: set it to${{ github.ref == 'refs/heads/main' }}so onlymainwrites the cache and PRs only read it. This avoids filling your quota with PR caches.cache-on-failure: set totrueto save the cache even when a test fails, so a red build still warms the next run.cache-all-cratesandcache-workspace-crates: both default tofalse. Turn the second one on only if you have a big workspace with stable inner crates.
- uses: Swatinem/rust-cache@v2
with:
shared-key: ci
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-on-failure: true
Turn off incremental compilation in CI
Incremental compilation saves rebuild time on a developer machine by storing extra data on disk. In CI you build once from a mostly clean state, so it adds overhead and large artifacts without payoff. Set it off for the whole job:
env:
CARGO_INCREMENTAL: 0
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
rust-cache already strips incremental data before saving, but CARGO_INCREMENTAL=0 also skips the cost of generating it. Treat -D warnings as optional; it makes warnings fail the build, which is a policy choice, and it changes the build flags so keep it the same in every job that shares a cache.
Compiler caching with sccache
mozilla-actions/sccache-action adds a second layer: sccache caches individual rustc outputs. Per its README, set SCCACHE_GHA_ENABLED: "true" and RUSTC_WRAPPER: "sccache" in env, and add the action step:
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: mozilla-actions/sccache-action@v0.0.11
sccache and rust-cache overlap. Use rust-cache first. sccache helps most in these cases: a large workspace where changing one crate invalidates a lot of target, builds of many different feature sets, or a team with a shared remote storage backend. It has limits: incremental compilation output is not cached (another reason to set CARGO_INCREMENTAL=0), and some crate types are not cacheable. Measure both ways before keeping the extra moving part.
Run tests with cargo-nextest
cargo-nextest runs each test in its own process and schedules them across all cores. It is often faster than cargo test on suites with many test binaries, and it supports partitioning for sharding. The nextest docs show installing it with taiki-e/install-action:
- uses: taiki-e/install-action@v2
with:
tool: nextest
- run: cargo nextest run --workspace --profile ci
Note that nextest does not run doctests, so keep cargo test --doc as a separate step. For sharding across jobs, nextest has --partition count:1/4 (also hash: mode); put the index in a matrix. See test sharding in GitHub Actions.
Linker and build profile
- Faster linker. Linking is single-threaded and slow on big binaries. Using
lldormoldthroughRUSTFLAGSor.cargo/config.tomlcan cut link time noticeably. Recent stable toolchains on x86_64 Linux may already ship a faster linker by default, so check your version's release notes before adding flags. - Debug info. Set
[profile.dev] debug = "line-tables-only"ordebug = falsefor CI builds. Less debug info means less to write and link. - Check before build.
cargo clippyalready type-checks. Run it in a job alongside tests instead of runningcargo checkandcargo buildback to back. - Release builds. Only build
--releasewhere you ship.lto = "thin"is cheaper than"fat".
What more CPU cores buy you
rustc parallelizes across crates and codegen units, and nextest parallelizes across tests, so Rust scales with cores until the link step. Our benchmarks have one Rust row: React + Rust (Axum) + Postgres went from 2m37s on a GitHub-hosted runner to 1m45s on compiler.dev (1.5x), a full CI run with 10 test shards and a real database, measured 8 October 2026. That is lower than the Go and Gradle stacks, and a warm cache is part of why: a cached build has little left to compile.
You can try your own workflow by changing one line to runs-on: [self-hosted, Linux, compiler-ci]. See pricing and the runner size picker. Related: the Rust template, GitHub Actions caching and Docker layer caching for container builds.
FAQ
Do I need both rust-cache and sccache?
Usually not. Start with rust-cache. Add sccache if large workspaces or many feature combinations still rebuild too much.
Why set CARGO_INCREMENTAL=0 in CI?
Incremental data helps repeated local rebuilds. In CI it adds disk writes and cache size without helping a clean build.
Is cargo-nextest faster than cargo test?
Often, on suites with many tests, because it schedules tests across cores in separate processes. It skips doctests, so run those separately.
How do I stop PRs from filling the cache?
Use save-if: ${{ github.ref == 'refs/heads/main' }} on rust-cache, so PRs restore but never save.
Related
- Add a CI Speed Badge to Your READMEShow how fast your CI is with a README badge: the median GitHub Actions run time for your repository, updated automatically. Free for public repos.
- Cache Cargo and Rust Builds in GitHub ActionsCache the Cargo registry, git dependencies and target directory in GitHub Actions with Swatinem/rust-cache or actions/cache, and avoid bloated caches.
- Speed Up .NET in GitHub ActionsSpeed up .NET builds in GitHub Actions: NuGet caching with setup-dotnet, --no-restore, --no-build, parallel dotnet test, and SQL Server services.
- Speed Up Bazel in GitHub ActionsSpeed up Bazel in GitHub Actions with setup-bazel, a disk cache, repository cache, a remote cache, and the right --jobs setting. Working YAML.