Skip to main content

Cache

Cache configuration describes what directories and files to preserve between workflow runs, and how cache keys are computed. Caching expensive-to-recompute directories (package manager stores, build caches) can dramatically reduce subsequent run times.

For runtime cache behavior (where hit/miss shows up, host vs Docker constraints), also see:

Quick example

check:
stage: ci
target: linux
cache:
key:
prefix: loom-cache
files:
- pnpm-lock.yaml
paths:
- .pnpm-store
- .nx/cache
policy: pull-push
when: always
script:
- pnpm install --frozen-lockfile
- pnpm nx run-many -t check

Schema rules

The schema validator enforces these constraints (verified via loom check):

Cache forms

FormDescription
Mapping (single cache)A single cache configuration applied to the job.
Sequence (multiple caches)A list of named cache entries, each operating independently.
null or []Explicitly disables caching for the job, overriding any default.cache.

Cache subkeys

SubkeyRequiredDescription
pathsYes (unless disabled: true)Non-empty list of directories/files to cache.
keyNoCache key as a string or structured mapping.
fallback_keysNoFallback keys tried in order if the primary key misses.
policyNoWhen to restore/save. Default: pull-push.
whenNoSave based on job status. Default: on_success.
nameOnly in sequence formUnique name identifying the cache entry.
disabledNo (sequence only)Disable a named cache entry without removing it.

Unknown keys produce: remove unknown cache key; allowed keys are name, disabled, paths, key, fallback_keys, policy, when.

paths

Non-empty list of non-empty strings. Each string is a path relative to the project workspace root.

cache:
paths:
- .pnpm-store
- .nx/cache
- node_modules

For Docker jobs (image: set), paths refer to directories inside the container's workspace mount. Mismatches between host and container paths are a common source of "cache did nothing" issues — see Operational constraints.

key

Cache key determines which cached archive to restore and where to save. All runs that produce the same key share the same cached data.

String form

A non-empty string that supports template variable placeholders.

cache:
key: "pnpm-${job_name}-${head_sha}"
paths:
- .pnpm-store

Mapping form

A mapping with an optional prefix and a required files list.

cache:
key:
prefix: loom-cache
files:
- pnpm-lock.yaml
paths:
- .pnpm-store
SubkeyRequiredDescription
prefixNoNon-empty string prepended to the computed key. Supports template placeholders.
filesYesNon-empty list of file paths or glob patterns. When file contents change, a new cache key is generated.

Glob support in files: entries support *, ?, and ** (doublestar recursive semantics). For example, "**/go.sum" matches go.sum files in any subdirectory. This is a Loom extension — many other CI systems do not support glob patterns in cache key files.

cache:
key:
prefix: loom-cache-go
files:
- go.work
- go.work.sum
- "**/go.sum"
paths:
- .go

If key is omitted, the runtime uses a default key computation.

fallback_keys

Non-empty list of non-empty strings tried in order when the primary key misses. Supports template variable placeholders.

cache:
key: "pnpm-${head_sha}"
fallback_keys:
- "pnpm-main"
- "pnpm-default"
paths:
- .pnpm-store

policy

Controls when the cache is restored and saved.

ValueRestore at startSave after finishUse case
pull-push (default)YesYesStandard: restore and update the cache each run.
pullYesNoRead-only: parallel jobs that consume but don't update the cache.
pushNoYesWrite-only: jobs that build/populate the cache for others.
install:
stage: deps
target: linux
cache:
paths: [.pnpm-store]
policy: pull-push
script:
- pnpm install

lint:
stage: ci
target: linux
cache:
paths: [.pnpm-store]
policy: pull
script:
- pnpm run lint

In this example, install both restores and saves the cache. lint only restores it, avoiding a redundant save since the cache contents didn't change.

when

Controls when the cache is saved based on the job's exit status.

ValueBehavior
on_success (default)Save only when the job succeeds.
on_failureSave only when the job fails.
alwaysSave regardless of exit status.
check:
stage: ci
target: linux
cache:
paths: [.pnpm-store]
when: always
script:
- pnpm install
- pnpm test

Use when: always when the cached artifacts (like installed dependencies) are valid regardless of whether the job's tests pass or fail.

name

Required when cache is a sequence. Each entry must have a unique name.

Duplicate names produce: use unique cache names; "xyz" is duplicated.

default:
cache:
- name: pnpm
key:
prefix: loom-cache
files: [pnpm-lock.yaml]
paths: [.pnpm-store]
policy: pull-push
when: always
- name: go
key:
prefix: loom-cache
files: [go.work, "**/go.sum"]
paths: [.go]
policy: pull-push
when: always

disabled

Set disabled: true in a sequence entry to disable that named cache without removing it. When disabled, paths is not required.

check:
stage: ci
target: linux
cache:
- name: pnpm
disabled: true
- name: go
paths: [.go]
script:
- go test ./...

Disabling cache entirely

To override a default.cache for a specific job, set cache to null or []:

default:
cache:
paths: [.pnpm-store]
policy: pull-push
when: always

build-image:
stage: deps
target: linux
cache: []
script:
- docker build -t myapp .

Cache key template variables

When cache.key is a string, or in cache.key.prefix and cache.fallback_keys entries, these placeholders are expanded at runtime:

PlaceholderDescription
${job_name}Job name (graph node id).
${job_id}Job id (same as job name today).
${run_id}Executor run id.
${pipeline_id}Executor pipeline id.
${head_sha}Snapshot HEAD commit SHA.
cache:
key: "pnpm-${job_name}-${head_sha}"
paths:
- .pnpm-store

Runtime behavior

During local execution, cache operations run as system sections around the selected job runtime provider (Host or Docker). Cache activity is recorded in structured runtime logs, not in your script: output.

Where cache events appear

Cache operations produce two system sections per job:

SectionTimingPurpose
cache_restoreBefore script runsRestores cached archives matching the key (or fallback keys).
cache_saveAfter script finishesSaves the cached paths if policy and when conditions are met.

These system sections appear in the runtime logs at:

.loom/.runtime/logs/<run_id>/jobs/<job_id>/system/cache_restore/events.jsonl
.loom/.runtime/logs/<run_id>/jobs/<job_id>/system/cache_save/events.jsonl

Follow the Diagnostics ladder to navigate from the pipeline summary to these events.

Cache store

Cached archives are stored locally under .loom/.runtime/cache/. Each archive is identified by a scope hash (derived from the repository remote URL or workspace path) and a key hash. The cache store tracks manifests that record the key, paths, and creation time for each archive.

Key design checklist

Use this checklist to avoid the most common "cache is useless" failures:

  • Pick stable paths: cache directories that are expensive to recompute and safe to reuse (package manager stores, build caches). Keep them workspace-relative (e.g., .pnpm-store, .nx/cache).
  • Choose key.files that fully describe the cache contents: include lockfiles and any configuration that changes the generated output (e.g., pnpm-lock.yaml, package.json, tool config).
  • Avoid key collisions: scope your key with ${job_name} or a distinct prefix so unrelated jobs don't overwrite each other. Use multi-cache (name) for different concerns.
  • Decide when to include ${head_sha}:
    • Include it when correctness matters more than reuse (per-commit caches, fewer stale mismatches).
    • Omit it when reuse matters more than strictness (branch/shared caches), but be prepared to invalidate keys when behavior changes.
  • Multi-cache: use one cache entry per concern (e.g., pnpm vs go) so you can invalidate and iterate independently.

Operational constraints

These are the constraints that show up as "cache didn't restore" or "cache saved but didn't help":

ConstraintImpact
PermissionsThe runtime must be able to read/write each cache.paths directory. For Docker jobs, the container user must have write permission inside the mounted workspace.
Disk usageLarge caches grow quickly and affect performance and local disk pressure. Cache fewer, high-value directories rather than the whole workspace.
Key stalenessIf cache keys don't change when inputs change, stale state is restored. Use key.files and ${head_sha} to keep invalidation aligned to real change.
Host vs Docker pathsHost jobs: paths are directories in your checkout. Docker jobs: paths are inside the container's workspace mount. Mismatches are the most common source of "cache did nothing".
Path safetyCache paths must be relative to the workspace. Absolute paths and paths containing .. are rejected.

Planned

  • Fully specified cache key computation and scoping rules.
  • Cache restore/save outcomes surfaced directly in receipts and summaries.
  • --cache-diff workflows to quarantine cache keys that induce mechanical divergence.