Skip to content
Remote cache

CI setup

GitHub Actions

The fastest way to add kache to a GitHub Actions workflow is the official action:

steps:
  - uses: actions/checkout@v4
  - uses: dtolnay/rust-toolchain@stable

  - uses: kunobi-ninja/kache-action@v1

  - run: cargo build --release

The action installs kache, sets RUSTC_WRAPPER=kache, and persists the local kache store with GitHub's built-in Actions cache by default. No S3 bucket is required.

The default GitHub Actions cache backend is the simplest choice for most open-source projects. For private infrastructure, larger caches, or sharing across repositories, use an S3 bucket instead.

Using S3 with kache-action

If you have an S3 bucket, pass it to the action:

steps:
  - uses: actions/checkout@v4
  - uses: dtolnay/rust-toolchain@stable

  - uses: kunobi-ninja/kache-action@v1
    with:
      s3-bucket: my-build-cache
      s3-region: eu-west-1
      s3-access-key-id: ${{ secrets.S3_ACCESS_KEY_ID }}
      s3-secret-access-key: ${{ secrets.S3_SECRET_ACCESS_KEY }}

  - run: cargo build --release
  - run: cargo test

For S3-compatible stores such as MinIO, Ceph, or R2, also set s3-endpoint:

- uses: kunobi-ninja/kache-action@v1
  with:
    s3-bucket: my-build-cache
    s3-endpoint: https://minio.internal:9000
    s3-access-key-id: ${{ secrets.S3_ACCESS_KEY_ID }}
    s3-secret-access-key: ${{ secrets.S3_SECRET_ACCESS_KEY }}

With S3 configured, the action starts the kache daemon for warm prefetch, saves a build manifest after the build, pushes new artifacts with kache sync --push, writes a job summary, and can post a sticky PR comment with kache report --format github.

Useful action inputs:

InputDefaultDescription
versionlatest releasekache version to install
github-cachetrueUse GitHub Actions cache when S3 is not configured
s3-bucketS3 bucket name; enables the S3 backend
s3-regionus-east-1S3 region
s3-prefixartifactsS3 key prefix
s3-endpointCustom endpoint for MinIO, Ceph, R2, etc.
syncfalseWith S3, pull the full remote cache during setup
warmtrueWith S3, prefetch likely artifacts from the saved build manifest
manifest-keytarget tripleScope saved manifests for different build shapes
pr-commenttruePost or update a sticky PR cache summary comment
max-size50GiBLocal store cap, mapped to KACHE_MAX_SIZE

Other CI systems

Outside GitHub Actions, configure kache directly via environment variables:

env:
  RUSTC_WRAPPER: kache
  KACHE_S3_BUCKET: my-build-cache
  KACHE_S3_REGION: us-east-1
  KACHE_S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
  KACHE_S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}

For AWS with IAM roles (OIDC), skip the access key variables and let kache use the standard credential chain.

For S3-compatible stores, also set KACHE_S3_ENDPOINT. KACHE_S3_PREFIX is optional and defaults to artifacts.

When no daemon is running in an ephemeral runner, use explicit sync steps:

- name: Warm cache
  run: kache sync --pull

- run: cargo build --release

- name: Push new artifacts
  run: kache sync --push
  if: always()

The if: always() on the push step stores newly built artifacts even when the build fails partway through. Partial cache population still speeds up future runs.

Both sync subcommands require a configured S3 remote. Without one they error with No remote configured. By default kache sync --pull is filtered to the crates in the current workspace's Cargo.lock, so an ephemeral runner only fetches what this build needs. Use kache sync --pull --all to pull every artifact in the bucket regardless of the local lockfile.

kache sync reports failed transfers as (N failed) on its progress line but still exits 0, so CI cannot detect partial transfer failures from the exit code alone. If that matters, scan the step's stderr for failed.

CI progress output

kache can print per-crate progress lines to stderr when KACHE_PROGRESS is set. It is silent by default — both in CI and locally — to avoid cargo caching the wrapper's stderr as stale compiler diagnostics. Set the variable to opt in:

ValueBehavior
unset / anything elseSilent (default)
1 or hitsPrint hits only
verbose or allPrint hits, dups, and misses

With KACHE_PROGRESS=verbose, lines look like:

[kache] serde: local hit (2ms, 1.2 MB)
[kache] tokio: local hit (3ms, 4.8 MB)
[kache] myapp: miss (1.4s, 892 KB)

At hits/1 only the local hit lines appear; miss and dup lines show up only under verbose/all. The format is [kache] <crate>: <label> (<elapsed>[, <size>]). There is no terminal or CI detection — output depends solely on this variable.

Coverage and instrumentation

Coverage tools like cargo-tarpaulin and cargo-llvm-cov use -C instrument-coverage, which changes what kache stores (path remapping is skipped to preserve source mapping for coverage reports). This produces different cache keys from regular builds, and those keys include the build's local path identity, so the coverage cache is isolated automatically — from regular builds and from builds at a different checkout path or tool root.

That said, many teams disable kache for coverage runs entirely to keep things simple:

- name: Test with coverage
  run: cargo tarpaulin --engine llvm --all-features --workspace --out Json
  env:
    RUSTC_WRAPPER: ""   # disable kache for this step

Disabling kache for specific steps

- name: Some step without caching
  run: cargo build
  env:
    KACHE_DISABLED: "1"

Only 1 or true (case-insensitive) disable kache. Other values — including 0, false, and empty — are ignored, so to re-enable kache in a later step you must unset the variable rather than set it to 0.

kache still strips the rustc -C incremental=… flags when disabled, which avoids APFS-related corruption in git worktrees on macOS — even when caching is off.

Available for:
Apple macOS logomacOSMicrosoft Windows logoWindowsLinux logoLinux
Download Kunobi