Skip to main content
A cache keeps directories of the workspace from one run to the next: the pnpm store, the Go module and build caches, a Cargo target/, pip wheels. Declare it on the stage that fills it, and the next run of that stage starts warm.
That is all a stage needs. The rest of this page explains what happens around it, so you can trust a cache instead of guessing about it.

The life of a cache

  1. The key is computed after checkout. ${{ hashFiles('pnpm-lock.yaml') }} becomes the SHA-256 of the matching files, read in the stage’s working_directory. The key changes exactly when the lock file does.
  2. Restore runs before the step. The exact key wins. Otherwise each restore_keys prefix is tried in order, and the newest stored entry that starts with it is restored. A cache that changed slightly still starts nearly warm.
  3. Save runs only after the step succeeds. A failed, cancelled or timed-out step saves nothing, so a broken install never becomes the next run’s starting point.
  4. A key with hashFiles is saved once. When the exact key was restored, its content is already stored. A key with no hashFiles is saved again after every successful step, and the newest save wins.

Trust scopes: a branch can never poison main

A cache is a way for one run to put files into another run’s workspace. Ankra treats that as a security boundary, not just a performance feature.
  • A run on the default branch restores only what default-branch runs saved.
  • Any other run (a feature branch, a pull request, a manual run of another ref) looks in its own scope first and then falls back to the default branch’s. A new pull request starts warm from main.
  • Nothing a branch saves ever reaches a default-branch run. A less-trusted ref cannot plant a poisoned dependency in what your release build restores.
  • A restricted fork run and a tag run get no shared cache at all. Every declared path starts empty, and the step records caches are not shared with fork runs or caches are not shared with tag runs.
  • Scopes are per repository and cluster. One repository never restores another’s cache.
cache is a protected section. A pull request can rewrite its scripts, but it cannot change which earlier output is restored into the workspace until the change reaches the default branch and an administrator approves it.

Writing good keys

hashFiles patterns are relative and /-separated. At most one ** may stand immediately before the file name, and the file name may use *. One call takes up to eight patterns, and files under .git are never hashed. A pattern that matches no file adds nothing to the key. A key Ankra cannot read this way is used as written, and the step’s cache note says so.
Point the toolchain at the workspace. A cache path is relative to the workspace (/workspace). Set the tool’s own cache variable in the stage’s env, for example GOMODCACHE=/workspace/.ankra-go/mod, GOCACHE=/workspace/.ankra-go/cache, PNPM_STORE_DIR, npm_config_cache, UV_CACHE_DIR, PIP_CACHE_DIR or CARGO_HOME. A tool that writes to $HOME writes outside the workspace, and nothing is kept.

What Ankra generates for you

The pipeline a new application’s setup pull request commits already declares its test stage’s caches. They use fallback: "none", live in directories under .ankra- that the scan step skips, and point the toolchain at them through env. A pipeline you already committed is never rewritten. Copy the block below into it to get the same. Each key’s restore_keys is its own prefix (pnpm-, go-, and so on). In a monorepo, each component’s directories carry the component’s name (.ankra-node-cache-web/... for web/), because its test stage runs beside its siblings’.

Recipes

GOCACHE holds compiled packages and test results. Restoring it is what lets go test print (cached) for packages your change did not touch.

Did my cache work?

Every step records one result for all its caches, and one entry per cache. ankra pipeline get shows the result in the CACHE column, and the portal’s run timeline shows it as a chip on the step.
A failed restore never fails the step. The step starts cold and logs a line starting [ankra] cache that says why: the key could not be computed (key_unresolvable), the archive did not download or unpack, or the restore pod was not scheduled or timed out.

Limits and retention

On an older agent. Caches are stored as archives in the vault on clusters whose agent supports it. On an older agent, fallback: volume (the default) gives each path one 5Gi volume shared by runs of the same trust scope on that cluster, and fallback: none gives the step an empty directory. Prefer none for a cache that is only an optimisation: a shared volume attaches to one node at a time, so two overlapping runs could wait on each other. Upgrade the agent (ankra cluster agent upgrade) to get archives.

Image builds cache their layers for you

A build stage needs no cache: block. Every image build imports and exports a BuildKit layer cache in your organisation’s CI staging repository, next to the images it builds, one per component. An unchanged RUN pnpm install layer is pulled, not rebuilt.
  • The cache is written only by runs that may push. A restricted fork run neither reads nor writes it.
  • A failed cache write never fails the build.
  • build.cache_to in your pipeline is used only when no organisation cache could be resolved.
To get the most from it, order your Dockerfile so what changes least comes first: copy the lock file and install dependencies before you copy the source.

Set a cache once for every stage

defaults.cache applies to every stage that declares no cache of its own, the same way defaults.image and defaults.network do.

Caching is one of three ways to save time

A warm cache makes the work you run faster. Two other features stop you running work at all:
  • Affected-only stages skip tests that a change cannot reach, using your Go or pnpm dependency graph.
  • Promotion publishes the image a pull request already built and tested, instead of rebuilding it on merge.
Every run records the time all three saved. ankra pipeline time-saved totals it for your organisation.