> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revyl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> Persist dependencies and build state between remote builds

Every remote build runs in a clean, single-use VM. Without caches, that means
a full dependency install and a cold compile every time. Caches persist the
directories you choose between builds, so the second build is much faster than
the first.

## Configuring caches

Declare caches under `build.platforms.<key>.caches`. Each cache has a `key`
and a list of project-relative `paths` stored under it:

```yaml theme={null}
build:
  platforms:
    ios:
      caches:
        - key: ios-deps
          paths:
            - ios/Pods
            - build              # DerivedData from -derivedDataPath build
    android:
      caches:
        - key: gradle
          paths:
            - android/.gradle
```

Paths must be project-relative and stay inside the workspace. Absolute paths
and `..` segments are rejected.

## How it works

Each cache key maps to a dedicated **cache disk** that Revyl stores between
builds:

1. When a build starts, the disk for each configured key is fetched and attached to the build VM. A key that has never been saved gets a fresh, empty disk (20 GB).
2. Before your commands run, each configured path is linked onto its cache disk. Whatever your build writes there, like `node_modules`, `Pods`, or DerivedData, lands on the disk.
3. If the build **succeeds**, the disks are compressed and stored for the next build. Failed builds don't update caches, so a broken run can't poison them.

Because a cache is a real disk rather than a copied-in archive, restoring a
populated cache doesn't scale with file count. A `node_modules` tree with
hundreds of thousands of files costs the same to attach as an empty one.

### Scope and sharing

Cache keys are scoped to your organization. Any build in your org that names
the same key gets the same cache across apps, platform streams, branches,
and whoever (or whatever CI job) triggered the build. Use distinct keys when
contents must not mix, e.g. `ios-deps` vs `android-gradle`.

### Concurrency

Concurrent builds using the same key each get a private copy of the disk;
they never see each other's in-flight writes. On success, the last build to
finish wins, the same semantics as GitHub Actions caches. There's no locking,
and there doesn't need to be: a lost cache write only costs the next build
some warm-up time.

## Skipping caches

```bash theme={null}
revyl build --remote --platform ios --no-cache
```

Runs a single build without restoring **or** saving caches. Use it to
reproduce a cold build, or to rebuild past a corrupted cache. The next
regular build starts from the last saved state, so one `--no-cache` run
doesn't reset anything. To actually discard a cache, change the key.

## What to cache

Good candidates are directories that are expensive to recreate and safe to
reuse:

* **JS dependencies:** `node_modules`
* **CocoaPods:** `ios/Pods`
* **Xcode DerivedData:** the `-derivedDataPath` directory (e.g. `build`), for incremental compiles
* **Gradle project state:** `android/.gradle`

Two caveats:

* Only project-relative paths can be cached. Tool caches that default to the home directory (like Gradle's `~/.gradle` dependency cache) need to be pointed into the project first. For example, use `GRADLE_USER_HOME=$PWD/.gradle-home ./gradlew assembleRelease` with `.gradle-home` in `paths`.
* If you cache the directory your artifact is built into (DerivedData), keep `output` specific enough that a stale product from a previous build can't match the glob.

## Related

* [Configuration](/remote-builds/configuration): the full `build:` reference
* [Toolchain Images](/remote-builds/toolchains): what's preinstalled, so you don't cache what's already there
