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

# Warming

> Pre-populate Aspect Workflows CI runner caches with a scheduled aspect ci warming job, so new runners start with external repositories already fetched.

export const gatedAccess = (user, group) => {
  const loggedIn = !!(user && user.loggedIn);
  const groups = user && user.tenantMetadata && user.tenantMetadata.docsGroups || [];
  if (loggedIn && (!group || groups.indexOf(group) >= 0)) {
    return "entitled";
  }
  return loggedIn ? "signed-in" : "anonymous";
};

export const GatedLink = ({access, href, group, children}) => {
  const note = group ? "Aspect Enterprise customers" : "free Aspect account";
  const muted = {
    fontSize: "0.85em",
    opacity: 0.7,
    whiteSpace: "nowrap"
  };
  if (access === "entitled") {
    return <a href={href}>{children}</a>;
  }
  if (access !== "signed-in") {
    return <span>
        <a href={"/login?redirect=" + encodeURIComponent(href)}>{children}</a>
        <span style={muted}> (sign in: {note})</span>
      </span>;
  }
  return <span>
      {children}
      <span style={muted}> ({note})</span>
    </span>;
};

<Info>
  <code>aspect ci warming</code> requires Aspect CLI **[v2026.39.10](https://github.com/aspect-build/aspect-cli/releases/tag/v2026.39.10) or newer** (pinned via <code>.aspect/version.axl</code>). For the legacy <code>rosetta run warming</code> task, see <GatedLink access={gatedAccess(user, "workflows-subscriber")} href="/docs/aspect-workflows/enterprise/legacy/yaml-tasks/warming" group="workflows-subscriber">Cache warming (legacy)</GatedLink>; to move over, follow the [migration guide](/docs/cli/migration/warming).
</Info>

## How warming works

Aspect Workflows CI runners are persistent: a runner reuses its caches across the jobs it serves. When a runner group scales up, a new runner starts cold, and its first job has to fetch every external repository and run each repository rule before Bazel can analyze anything. Warming front-loads that work:

1. A scheduled warming job, typically on `main`, runs `aspect ci warming`. The task runs the runner health check, clears the previous Bazel state on the runner's storage mount, and runs `bazel build --nobuild` on the target patterns. That fetches external repositories, runs repository rules, and loads and analyzes the targets; no build actions execute. The task then uploads the repository cache, output base, and Bazel caches as the archive for the runner group's warming set.
2. A new runner restores the latest archive for its warming set while it boots. The first job's health check waits for the restore to finish before Bazel starts. To hold the CI agent until the restore finishes instead, set `wait_for_warming`, or ask Aspect to on a deployment hosted by Aspect (off by default; see <GatedLink access={gatedAccess(user, "workflows-subscriber")} href="/docs/aspect-workflows/enterprise/self-hosted/configuration/warming" group="workflows-subscriber">Warming configuration</GatedLink>).
3. That first `bazel build` or `bazel test` starts with external repositories fetched and repository rules run.

Warming doesn't populate the remote cache: build outputs still come from the [remote cache](/docs/aspect-workflows/platform/features/remote-cache) or are built by the job.

An archive restores only on runners booted from the same machine image it was created on, so after a runner image upgrade, new runners boot cold until the next warming job runs. The archive also carries the Aspect CLI's download caches, so a warm runner doesn't re-fetch the CLI.

## Turning it on

Warming is enabled per runner group, and each group that needs different caches gets its own warming set: one for a group that also restores a pnpm store or Poetry environments, another for a group on a different CPU architecture.

On a deployment [hosted by Aspect](/docs/aspect-workflows/enterprise/hosted/overview), [ask for](/docs/aspect-workflows/enterprise/hosted/requesting-changes) the groups you want warmed. On a self-hosted deployment, set `warming = true` on the group in Terraform; see <GatedLink access={gatedAccess(user, "workflows-subscriber")} href="/docs/aspect-workflows/enterprise/self-hosted/configuration/warming" group="workflows-subscriber">Warming configuration</GatedLink>.

## Configure the warming job

Add a scheduled job that runs `aspect ci warming` on the runner group you want warm, against your main branch, once or twice a day. Each warming set needs its own job, on a runner group that uses that set.

With no target patterns, the task warms `...`, every target in the package at and beneath the current directory. The examples pass `//...` explicitly, and schedule the job daily at 12:00 UTC (`0 12 * * *`); adjust the time to your team's working hours.

For the full set of flags and target options, see [aspect ci warming](/docs/cli/tasks/ci_warming).

<Info>
  The warming task doesn't need the <code>setup-aspect</code> step: it runs on an Aspect Workflows CI runner, where <code>aspect</code> is already on the <code>PATH</code>. The GitHub Actions example still uses the action because it provides the auth <code>aspect</code> needs to post its status check; on other providers, supply <code>ASPECT\_API\_TOKEN</code> instead (see the comments in the examples).
</Info>

<Tabs>
  <Tab title="GitHub Actions">
    ```yaml title=.github/workflows/aspect-workflows-warming.yaml theme={null}
    name: Aspect Workflows Warming

    on:
      schedule:
        - cron: '0 12 * * *'
      workflow_dispatch:

    permissions:
      contents: read
      id-token: write

    jobs:
      warming:
        runs-on: [self-hosted, aspect-workflows, aspect-default]
        steps:
          - uses: actions/checkout@v6
          - uses: aspect-build/setup-aspect@ebca96eb49ef58c00d4226de8bd3a0813507dd87 # v2026.38.3
            with:
              aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
          - run: aspect ci warming --task:name warming -- //...
    ```
  </Tab>

  <Tab title="Buildkite">
    ```yaml title=warming-pipeline.yaml theme={null}
    # ASPECT_API_TOKEN is a Buildkite secret.
    steps:
      - label: ":fire: Warming"
        agents:
          queue: aspect-default
        command: aspect ci warming --task:name warming -- //...
        secrets: [ASPECT_API_TOKEN]
    ```

    Create a new Buildkite pipeline with these steps, then open its **Pipeline Settings > Schedules > New Schedule** and set the cron interval to `0 12 * * *`.
  </Tab>

  <Tab title="GitLab CI">
    ```yaml title=.gitlab-ci.yml theme={null}
    # Set ASPECT_API_TOKEN as a masked CI/CD variable in Settings > CI/CD > Variables.
    stages:
      - CI

    warming:
      tags: [aspect-workflows, aspect-default]
      stage: CI
      rules:
        - if: '$CI_PIPELINE_SOURCE == "schedule"'
      script:
        - aspect ci warming --task:name warming -- //...
    ```

    If your `.gitlab-ci.yml` has `workflow: rules`, they must let scheduled pipelines through, or the schedule creates no pipeline. Keep your other jobs off the schedule with their own `rules`, as the [GitLab CI/CD pipelines](/docs/aspect-workflows/enterprise/connect/ci-pipelines/gitlab) example does.

    To schedule it, open **Build > Pipeline schedules > New schedule** in your project:

    1. Set the description to **Aspect Workflows Warming**.
    2. Set **Interval pattern** to `0 12 * * *` with **Cron timezone** set to UTC.
    3. Under **Select target branch or tag**, pick the branch to warm from.
    4. Check **Activated** and click **Create pipeline schedule**.
  </Tab>

  <Tab title="CircleCI">
    ```yaml title=.circleci/config.yml theme={null}
    # Set ASPECT_API_TOKEN as a project environment variable or context secret.
    workflows:
      aspect-workflows-warming:
        jobs:
          - warming
        when:
          and:
            - equal:
              - scheduled_pipeline
              - << pipeline.trigger_source >>
            - equal:
              - aspect-workflows-warming
              - << pipeline.schedule.name >>

    jobs:
      warming:
        machine: true
        resource_class: YOUR-ORG/aspect-default
        working_directory: /mnt/ephemeral/workdir
        steps:
          - checkout
          - run:
              name: Warming
              command: aspect ci warming --task:name warming -- //...
    ```

    Add a scheduled trigger in CircleCI (**Project Settings > Triggers**) named `aspect-workflows-warming`, with the cron schedule `0 12 * * *`. Guard your other workflows with `when: not: equal: [scheduled_pipeline, << pipeline.trigger_source >>]` so the schedule runs only the warming job, as the [CircleCI pipelines](/docs/aspect-workflows/enterprise/connect/ci-pipelines/circleci) example does.
  </Tab>
</Tabs>
