Skip to content
Dev Tools Advanced Tutorial

Speed Up Rust Compile Times with sccache and cargo-nextest

Diagnose slow Rust builds, then cache compilation and parallelize tests locally and in GitHub Actions.

Lenn Voss
Lenn Voss
Cloud & Infrastructure Writer · Aug 14, 2026 · 6 min read
Speed Up Rust Compile Times with sccache and cargo-nextest

What you'll build

You'll take a slow-compiling Rust workspace, find out where the time actually goes, then wire up sccache to cache compiler output (locally and in GitHub Actions) and cargo-nextest to run tests in parallel with per-test process isolation. The result: warm rebuilds that skip most of the compiler work, and a CI job that caches across runs.

Prerequisites

  • Rust 1.97.1 (current stable) installed via rustup. Verified against sccache v0.17.0 and cargo-nextest 0.9.143.
  • macOS or Linux (commands below; sccache and nextest both ship Windows binaries too).
  • A Cargo workspace to test on — any project with a few dozen dependencies shows the effect clearly.
  • For the CI section: a repo on GitHub. The GHA cache backend needs sccache ≥ 0.10, since GitHub shut down the legacy cache service in April 2025.

1. Diagnose where the time goes

Before caching anything, measure. Cargo has a built-in profiler:

cargo clean
cargo build --timings

This writes an HTML report to target/cargo-timings/cargo-timing.html. Open it and look at two things: which crates dominate the critical path (wide bars), and how much of the build is dependencies versus your own code. sccache helps most when the bulk is dependency compilation — which for a cold build, it almost always is.

2. Install sccache

sccache is a compiler-cache daemon: it wraps rustc, hashes each compilation's inputs, and serves the object output from cache on a hit.

# macOS
brew install sccache

# Any platform (builds from source, takes a few minutes)
cargo install sccache --locked

Prebuilt binaries for Linux (musl), macOS, and Windows are on the releases page if you'd rather not compile it. Confirm the version:

sccache --version
# sccache 0.17.0

3. Wire sccache into Cargo

Tell Cargo to route every rustc invocation through sccache. The persistent way is ~/.cargo/config.toml (create it if it doesn't exist):

[build]
rustc-wrapper = "sccache"

That's it for local use — the default backend is a 10 GiB local disk cache (~/.cache/sccache on Linux, ~/Library/Caches/Mozilla.sccache on macOS), tunable with SCCACHE_DIR and SCCACHE_CACHE_SIZE.

One caveat you must understand: sccache can't cache incrementally-compiled crates. Cargo enables incremental compilation for your workspace crates in dev builds, so those still compile normally — but dependencies aren't built incrementally, so sccache caches them regardless. That's the right trade-off locally. In CI, where every build starts cold, disable incremental entirely so everything is cacheable:

export CARGO_INCREMENTAL=0

Prove it works with a worst-case rebuild:

cargo clean && cargo build          # cold: populates the cache
cargo clean && cargo build          # warm: dependencies come from cache

The second build should be dramatically faster — the compiler is now only really working on your workspace crates.

4. Install cargo-nextest

Compilation is half the cycle; the other half is running tests. cargo-nextest is a drop-in test runner that executes every test in its own process, schedules them across all cores, and gives you clean pass/fail output plus retries and per-test timeouts — things cargo test can't do.

# macOS (universal binary)
curl -LsSf https://get.nexte.st/latest/mac | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin

# Linux x86_64
curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin

# Or from source
cargo install cargo-nextest --locked

Run your suite:

cargo nextest run

Useful flags: -p <package> to scope to one crate, --test-threads=N to cap parallelism, --no-fail-fast to keep going after a failure. Note that nextest doesn't run doctests — keep cargo test --doc alongside it.

5. Add a CI profile for nextest

Nextest reads .config/nextest.toml from the workspace root. Give CI different behavior than local runs — run everything, retry flaky tests, and flag slow ones:

[profile.ci]
fail-fast = false
retries = 2
slow-timeout = "60s"

Select it with cargo nextest run --profile ci (or the NEXTEST_PROFILE environment variable). Locally you keep the default profile's fail-fast behavior; CI gets the full picture in one run.

6. Put both into GitHub Actions

sccache's GHA backend stores the compiler cache in GitHub's Actions cache service, so it survives across workflow runs — including on ephemeral runners. The official sccache-action installs sccache and exposes the auth tokens the backend needs.

.github/workflows/ci.yml:

name: ci
on: [push, pull_request]

env:
  CARGO_INCREMENTAL: "0"
  SCCACHE_GHA_ENABLED: "true"
  RUSTC_WRAPPER: "sccache"

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: rustup update stable
      - name: Run sccache-action
        uses: mozilla-actions/sccache-action@v0.0.11
      - name: Install nextest
        run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
      - run: cargo nextest run --profile ci
      - run: cargo test --doc
      - name: Cache stats
        run: sccache --show-stats

The first run populates the cache; subsequent runs on the same dependency set should show a high hit rate in the final step. To invalidate the whole cache deliberately, set SCCACHE_GHA_VERSION to a new value.

Verify it works

Locally, after the warm rebuild from step 3, check the daemon's stats:

sccache --show-stats

Expected shape (numbers vary with your dependency count):

Compile requests                      247
Compile requests executed             235
Cache hits                            221
Cache hits (Rust)                     221
Cache misses                           14
Cache hits rate                     94.04 %
...
Cache location                Local disk: "/home/you/.cache/sccache"
Max cache size                     10 GiB

A warm rebuild with hits above ~90% means it's working. For nextest, a passing run ends with a summary line like:

    Starting 48 tests across 6 binaries
        PASS [   0.021s] my-crate config::tests::parses_defaults
        ...
------------
     Summary [   1.847s] 48 tests run: 48 passed, 0 skipped

In CI, the Cache stats step should show a near-zero hit rate on the first run and a high one on the second.

Troubleshooting

error: could not execute process sccache -vV (never executed)No such file or directory (os error 2) Cargo can't find the binary named in rustc-wrapper. Either sccache isn't installed or ~/.cargo/bin isn't on PATH for that shell. Run which sccache and fix your path, or put the absolute path in config.toml.

Cache hit rate stays near 0% and --show-stats lists incremental under non-cacheable reasons Incremental compilation is on, and sccache passes those requests straight through to rustc. Set CARGO_INCREMENTAL=0 (at minimum in CI) and rebuild.

sccache: error: Server startup failed: cache storage failed to read: Unexpected (permanent) in GitHub Actions, mentioning "This legacy service is shutting down" You're on a pre-0.10 sccache talking to GitHub's decommissioned legacy cache service. Update mozilla-actions/sccache-action to v0.0.11, which installs a current sccache.

Doctests never appear in nextest output Not a bug — nextest runs only test binaries; doctests are compiled and run by rustdoc via a separate path. Keep a cargo test --doc step, as in the workflow above.

Next steps

  • Team-scale caching: point sccache at shared storage — S3, GCS, Redis, and more are covered in the configuration docs — so one developer's cold build warms everyone's cache.
  • sccache 0.17's new client-side mode (SCCACHE_CLIENT_SIDE) does cache lookups in the client instead of the daemon, which cuts round-trips on busy workstations.
  • Shard huge suites across CI machines with nextest's --partition, and archive compiled tests with cargo nextest archive to split the build and run phases onto different runners.
  • Re-run cargo build --timings after all this — whatever's left on the critical path is your own code, and that's a job for crate-splitting, not caching.

Sources & further reading

  1. sccache README — github.com
  2. sccache Rust docs — github.com
  3. sccache GitHub Actions backend docs — github.com
  4. cargo-nextest installation docs — nexte.st
  5. cargo-nextest configuration docs — nexte.st
  6. sccache-action README — github.com
Lenn Voss
Written by
Lenn Voss · Cloud & Infrastructure Writer

Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading