# CI Cache Configuration

The `ci.cache` section of `atmos.yaml` configures the CI build cache. The cache restores a
well-known cache directory (the toolchain install path and anything else under the Atmos
cache root) at the start of a step and saves it back at the end, using the active CI
provider's cache store — the same store that `actions/cache` uses.

> ⚠️ Experimental

## Quick Start

**File:** `atmos.yaml`

```yaml
ci:
  cache:
    enabled: true     # master switch (required)
    auto: both        # restore on start AND save on end automatically
```

With this configuration, Atmos automatically restores the cache when it starts and saves it
when it exits (including on `SIGINT`/`SIGTERM`). You can also drive the cache explicitly with
the [`atmos ci cache`](/cli/commands/ci/cache) subcommands regardless of the `auto` setting.

## How It Works

The cache operates on a single well-known directory — the Atmos XDG cache root
(`~/.cache/atmos` by default). The toolchain is installed as a sub-path of this root, so a
single cache captures it. The cache key defaults to a value derived from the toolchain
lockfile (`toolchain.lock.yaml`) plus the OS and architecture, with a prefix restore-key
fallback — mirroring `actions/cache`.

Cache entries are **write-once**. To keep automatic and manual usage from conflicting, Atmos
records how each key was restored and whether it was saved: a save is skipped when the exact
key was an exact hit at restore time (content unchanged) or has already been saved. This is
the same `cache-hit ⇒ skip save` behavior as `actions/cache`.

:::note Requirements
Saving and restoring content require running inside a supported CI provider (GitHub Actions
today), which provides the runtime cache credentials (`ACTIONS_RUNTIME_TOKEN` and
`ACTIONS_RESULTS_URL`). GitHub **withholds these credentials from `run:` steps**, so
`atmos ci cache` can't reach the cache API directly from a `run:` step — wiring it up inside a
job always takes one of the [approaches below](#github-actions-integration). Outside a
supported CI provider, the cache is a no-op.
:::

## Configuration Reference

- **`ci.cache.enabled`**

  Master switch for the CI cache. The automatic hooks and the `atmos ci cache` subcommands
  only run when this is `true`.

  **Default:** `false`
- **`ci.cache.auto`**

  Automatic behavior. One of:
  - `off` — no automatic behavior; use the subcommands manually.
  - `restore` — restore on startup only.
  - `save` — save on exit only.
  - `both` — restore on startup and save on exit.
  **Default:** `off`
- **`ci.cache.root`**

  Override the well-known cache directory that is archived. Honors
  `ATMOS_XDG_CACHE_HOME` / `XDG_CACHE_HOME` when unset.

  **Default:** the Atmos XDG cache directory (e.g. `~/.cache/atmos`)
- **`ci.cache.paths`**

  Root-relative subpaths to include in the cache. When empty, the entire cache root is
  cached.

  **Default:** the entire cache root
- **`ci.cache.key`**

  The cache key. Supports Go templates with `{{.OS}}`, `{{.Arch}}`, and a `hashFiles`
  function (e.g. `{{ hashFiles ".tool-versions" }}`).

  **Default:** a key derived from the `toolchain.lock.yaml` hash plus OS/arch
- **`ci.cache.restore_keys`**

  Prefix fallback keys tried (in order) when the exact key is absent, mirroring
  `actions/cache` restore-keys. Each entry also supports template syntax.

  **Default:** the default key's prefix (without the lockfile hash)
- **`ci.cache.compression`**

  Archive compression. Currently only `gzip` is supported.

  **Default:** `gzip`

## Full Example

**File:** `atmos.yaml`

```yaml
ci:
  cache:
    enabled: true
    auto: both
    key: "atmos-toolchain-{{.OS}}-{{.Arch}}-{{ hashFiles \"toolchain.lock.yaml\" }}"
    restore_keys:
      - "atmos-toolchain-{{.OS}}-{{.Arch}}-"
    paths:
      - toolchain
    compression: gzip
```

## GitHub Actions Integration

GitHub's Actions cache service needs runtime credentials (`ACTIONS_RUNTIME_TOKEN` and `ACTIONS_RESULTS_URL`) that GitHub **withholds from `run:` steps**, so `atmos ci cache restore`/`save` can't reach the cache API directly from a `run:` step. Every approach below is a workaround for that, and they split on a single decision: **do you let a native action store the cache, or use one of our actions to surface the credentials so Atmos's backend stores it?**

- **Native `actions/cache`** (the _composite_ and _explicit_ tabs) — Atmos only computes _what_ to cache (the key and paths from `ci.cache`); `actions/cache` already holds the credentials internally and does the storage. This exposes **no** runtime token to your job.
- **Atmos-managed** (the _scoped-token_ and _ambient-token_ tabs) — Atmos's own backend (`atmos ci cache restore`/`save`) does the storage via the GitHub Actions cache service. The [`github-runtime`](https://github.com/cloudposse/atmos/tree/main/actions/github-runtime) action surfaces the withheld credentials so those `run:` steps can reach it.

:::tip Recommended (most secure)
Prefer the **`actions/cache` composite** (or the **explicit** form) — both expose **no** runtime token. Security ranking: **no token (composite/explicit) > scoped token > ambient token**. If you need Atmos to own restore/save, prefer `mode: output` (scoped) over `mode: env` (ambient).
:::

### actions/cache · composite (recommended)

One `uses:` — Atmos derives the key/paths, native `actions/cache` stores them, **no runtime token**.

```yaml
- uses: cloudposse/atmos/actions/cache@v1   # pin to a release or SHA
- run: |
    atmos toolchain install --default helm/helm@${{ env.HELM_VERSION }}
    atmos toolchain env --format=github
```

**Pros:** no token; native isolation/eviction/cross-OS; least boilerplate.
**Cons:** one more Atmos action in the chain (it pins `actions/cache` internally).

### actions/cache · explicit

The same as the composite, written out — useful when you want explicit control.

```yaml
- name: Resolve Atmos cache key & paths
  id: atmos-cache
  run: atmos ci cache paths --format=github

- name: Cache Atmos toolchain
  uses: actions/cache@v4
  with:
    key:          ${{ steps.atmos-cache.outputs.key }}
    path:         ${{ steps.atmos-cache.outputs.path }}
    restore-keys: ${{ steps.atmos-cache.outputs.restore-keys }}

- run: atmos toolchain install --default helm/helm@${{ env.HELM_VERSION }}
```

**Pros:** no token; full control over the `actions/cache` step.
**Cons:** three steps to wire by hand.

### Atmos backend · scoped token

Atmos owns restore/save. `github-runtime` `mode: output` returns masked credentials as step outputs that you thread only into the cache steps.

```yaml
- uses: cloudposse/atmos/actions/github-runtime@v1
  id: ghr
- run: atmos ci cache restore
  env:
    ACTIONS_RUNTIME_TOKEN: ${{ steps.ghr.outputs.runtime-token }}
    ACTIONS_RESULTS_URL:   ${{ steps.ghr.outputs.results-url }}
- run: atmos toolchain install --default helm/helm@${{ env.HELM_VERSION }}
- if: always()
  run: atmos ci cache save
  env:
    ACTIONS_RUNTIME_TOKEN: ${{ steps.ghr.outputs.runtime-token }}
    ACTIONS_RESULTS_URL:   ${{ steps.ghr.outputs.results-url }}
```

**Pros:** Atmos fully owns restore/save; token masked and scoped to the cache steps.
**Cons:** the token still enters those steps; more YAML.

### Atmos backend · ambient token

The simplest Atmos-managed form. `github-runtime` `mode: env` exports `ACTIONS_*` to `$GITHUB_ENV` for every later step.

```yaml
- uses: cloudposse/atmos/actions/github-runtime@v1
  with:
    mode: env
- run: atmos ci cache restore
- run: atmos toolchain install --default helm/helm@${{ env.HELM_VERSION }}
- if: always()
  run: atmos ci cache save
```

**Pros:** least YAML for the Atmos-managed path; works for any `atmos ci cache` subcommand.
**Cons:** the runtime token is **ambient** to every later `run:` step in the job (largest blast radius).

The actions ship inside the Atmos repository, so pin them to an Atmos release (`@v1` moving major tag, `@vX.Y.Z`, or a commit SHA). Both actions' full sources are in [`actions/`](https://github.com/cloudposse/atmos/tree/main/actions) — audit them before pinning.

## Environment Variables

| Variable | Description |
|----------|-------------|
| `ATMOS_CI_CACHE_ENABLED` | Override `ci.cache.enabled` (`true`/`false`) |
| `ATMOS_CI_CACHE_AUTO` | Override `ci.cache.auto` (`off`/`restore`/`save`/`both`) |
| `ATMOS_CI_CACHE_KEY` | Override the cache key (`--key`) |
| `ATMOS_CI_CACHE_ROOT` | Override the cache root (`--root`) |
| `ATMOS_CI_CACHE_PATHS` | Override the cached paths (`--path`) |
| `ACTIONS_RUNTIME_TOKEN` / `ACTIONS_RESULTS_URL` | Provided automatically inside a GitHub Actions runner; required for the GitHub Actions cache backend |

## Related

- [`atmos ci cache`](/cli/commands/ci/cache) - Restore, save, list, and delete cache entries
- [CI Configuration](/cli/configuration/ci) - Configure CI integration
- [`atmos toolchain`](/cli/commands/toolchain/usage) - The toolchain that the cache warm-starts
