Skip to content
Build and shipProduct pagePricing

Cache

Frazer Cache is one cache per site that your runners, devboxes, agent sandboxes and serverless builds all share, so nothing is downloaded or compiled a hundred times. It runs on the same servers as the jobs it serves, on your own servers or on the Frazer fleet. Only trusted builds write what others read, every shared entry is signed by its writer, and a cache outage makes a build slower but never fails it.

Product overview: GitHub runners and Cache.

A cache belongs to a host project at one site (for example acme-infra at ovh-vint-hill) and is shared with the projects that use it. It holds four tiers:

Tier Holds Shared how Evicted by default
1. Dependencies Go modules, OCI images, npm, pnpm, PyPI and uv packages, Maven, crates Across every project the cache is shared with; the client verifies content (go.sum, lockfile hashes, digests) Unused for 30 days
2. Build outputs Action-to-result maps and their blobs: GOCACHEPROG, Bazel, sccache, Gradle, Maven, Turborepo, Nx, BuildKit, actions/cache Per namespace, under write rules Least recently used; unused for 14 days
3. Warm disks A disk per repository and branch family: Git objects, toolchains, node_modules and the pnpm store, uv cache, BuildKit state Cloned copy-on-write for each job; committed only by clean default-branch jobs and prebuilds Idle for 7 days; 3 generations kept
4. Node hot tier Blobs in use on this server, and the bases of warm-disk generations A local copy on each server’s NVMe, never the only copy At disk-pressure marks of 80, 90 and 95 %

Nothing in the cache is the only copy of anything a build needs: every tier can be rebuilt from upstream or from source. Caches are never shared between organizations, not even for public dependencies.

A read takes the first hit in this order: the job’s own overlay, its branch, the pull request’s base branch, the default branch. For Go, Bazel and sccache, whose keys hash every input, a read can also fall back to the organization-wide toolchain namespace that protected CI publishes, so two repositories share an entry only when they compiled the same thing with the same toolchain.

  • Cache: the service at one site, with its tiers, capacity, quotas and endpoints.
  • Cache policy: which ecosystems are on, who may write, the read order, quotas, eviction, pins, and what release builds may read.
  • Namespace: one slice of a tier: {tier}/{ecosystem}/{scope}/{toolchain}, for example build/go/acme/go1.27-linux-amd64.
  • Writer class: what a write is proven to come from (protected-ci, prebuild, pr, fork, agent, box, serverless, release).
  • Overlay: a private namespace for one pull request, agent session or devbox. Nobody else reads it, and it is never promoted.
  • Warm disk: a copy-on-write volume per repository and branch family; each commit of it is a generation.

Runner pools, devboxes, agent sandboxes and serverless builds each name a cache policy. Frazer then sets up each job or box to use the cache, with no change to your build scripts.

  1. Look at your site’s cache and its policy.

    Terminal window
    frazer cache get ovh-vint-hill
    frazer cache policies get acme-default -o yaml

    The policy is also on the console, under Infrastructure › Build › Cache in the host project.

  2. Point each consumer at the policy.

    Consumer Where you set the policy
    Runner pool --cache-policy on frazer githubrunners pools create
    Devboxes The devbox policy’s caches.policy
    Agent sandboxes The workspace template’s cachePolicy
    Serverless builds The project setting build.cachePolicy
  3. Check what a writer may do before you rely on it. This asks what a pull request in acme/storefront may read and write:

    Terminal window
    frazer cache policies test acme-default --as oidc:acme/storefront@refs/pull/611/merge
  4. Run a few jobs, then open the cache page or the job’s Cache tab. Each restore shows the tier that answered, the bytes and the seconds.

A policy, as frazer cache policies get acme-default -o yaml prints it:

ecosystems: [go, docker, actions-cache, pnpm, uv, turborepo]
writers:
shared: [protected-ci, prebuild]
protectedCi: { refs: [main, "release/*", "v*"], workflows: [".github/workflows/ci.yaml", ".github/workflows/release.yaml"] }
overlays: { pr: 2GiB, session: 5GiB, box: scratch, serverless: project }
forks: none
teamTier: off
scope: { fallback: [overlay, branch, base, default], orgToolchainNamespaces: [go, bazel, sccache] }
release: { readsBuildOutputs: false }
quotas: { site: { build: 2TiB, warmDisks: 1TiB }, perProject: 1.5TiB, perNamespace: 200GiB }
gc: { build: { lru: true, maxUnused: 14d }, deps: { maxUnused: 30d }, overlays: { afterClose: 7d }, warmDisks: { idle: 7d, generations: 3 } }
pins: [warm-disk checkout-api@main, warm-disk web@main, build/go/acme/go1.27-linux-amd64]

Each ecosystem speaks its own protocol to the cache, so your build tools need no plugin.

Ecosystem How a job or box uses it
actions/cache, setup-* actions, Docker type=gha A cache endpoint inside the runner VM answers GitHub’s cache calls and passes every other call (artifacts, logs) to GitHub unchanged. No workflow edits, and GitHub’s 10 GB repository limit does not apply.
Go GOPROXY points at the site’s proxy, and GOCACHEPROG=frazer-cacheprog shares build outputs (Go 1.24 or later).
Docker and BuildKit BuildKit state lives on the warm disk, so RUN --mount=type=cache persists. Main publishes a type=registry cache to a Frazer Registry cache repository with mode=max.
pnpm, npm, yarn The store lives on the warm disk with node_modules; packages come through the dependency tier.
uv, pip UV_CACHE_DIR lives on the warm disk; packages come through the dependency tier.
Bazel, Buck2, Pants A Remote Execution API cache at the site, behind the cache’s own authentication. Jobs that may not write run with --remote_upload_local_results=false.
Turborepo, Nx The cache speaks Turborepo’s and Nx’s remote cache APIs, with signatures on.
Gradle, Maven An HTTP build cache at the site; only writers push.
Rust, C and C++ sccache over WebDAV to the site’s store.
Tests Go and Bazel test results ride the build tier. Flaky marks and test timings live in runner insights.

In a runner job, the variables come preset:

Runner job environment
GOPROXY=https://cache.<site>.internal/go,direct
GOCACHEPROG=frazer-cacheprog
ACTIONS_CACHE_MODE=read # for pull requests, agents and forks

Only trusted builds write what everyone else reads.

Writer class Proven by May write Reads
protected-ci The CI provider’s OIDC token: repository, a protected ref, and a workflow on the policy’s allow-list Its repository’s shared namespaces, the organization toolchain namespace, warm-disk commits Everything the policy allows
prebuild The Frazer identity of the prebuild worker Shared namespaces, module snapshots, warm-disk generations Everything
pr OIDC event=pull_request from the same repository Its pull request’s overlay only Overlay, branch, base, default branch
fork OIDC head repository differs from the repository Nothing Dependencies; default-branch build outputs of public repositories
agent A Frazer Agents identity, or a pull request whose author is a bot or on your agent list Its session’s or pull request’s overlay Same as pr
box The devbox’s workload certificate Its own box tier on scratch; a team namespace when the team tier is on Shared, and its team’s namespace
serverless The project’s build identity The project’s namespace; shared only for protected branches Dependencies and the project’s namespace
release OIDC tag on a protected ref Shared outputs, for main to reuse Dependencies only
  • Stage, then publish on success. A job’s writes wait on the server and are published only when the job’s conclusion is success.
  • Overlays are never promoted. When a pull request merges, main’s CI rebuilds and publishes. Overlays expire 7 days after the pull request closes or the session ends.
  • actions/cache follows the same rule. A job that may not write gets ACTIONS_CACHE_MODE=read, and the endpoint enforces it anyway.
  • Release builds read dependencies only, so nothing an untrusted job could have influenced reaches a release.

Every publish and every refusal is a row in the cache’s Activity, with the writer class, identity, namespace and bytes.

Terminal window
frazer cache writes --refused --since 24h

Every shared entry records who wrote it. If one turns out to be bad, purge everything that writer published, or a whole namespace. The plan lists the jobs that read the affected entries.

Terminal window
frazer cache purge --writer run:acme/storefront/123456 --dry-run

A warm disk holds what a repository’s jobs need before their first step: Git objects, toolchains, package stores and build state. Each job gets a copy-on-write clone; only clean default-branch jobs and prebuilds commit a new generation back. Pull request clones are thrown away.

Terminal window
frazer cache volumes list --repo acme/storefront

The scheduler prefers the server that holds a repository’s newest generation. When that server is full, the job takes a clone over the network where the site has shared storage, or fills a fresh volume from the site’s store, which is slower and shows in the job’s timeline. Devboxes, agent sessions and runners of the same repository can start from the same generation.

Quotas apply per site and tier, per consumer project (its share of the cache), per policy, per namespace and per overlay. Eviction runs by age and least recent use on every tier, and by disk pressure on every server.

Pins keep named namespaces and warm disks from eviction, such as release toolchains or main of your key repositories. Pins count against quota.

A nightly hygiene job removes orphaned blobs, flattens clone chains, samples digests, scans new archive entries for secret patterns and compacts storage. You can also run it from the console.

  • Per-job tokens: the cache mints a token per job from its OIDC token, scoped to namespaces and to read or write, valid only for the job’s lifetime. Eviction happens by policy, never because a client floods the quota.
  • Signed entries: each action-to-result entry carries its writer class and identity and is signed with your organization’s cache key. Readers accept only the writer classes their policy allows. Blobs are content-addressed and verified on read.
  • Encryption and secrets: overlays and shared namespaces are encrypted at rest per organization. An entry that matches a known secret pattern is quarantined and becomes a Security Center finding.
  • Sharing: sharing a cache with another project, with a byte limit for that project, needs the cache.caches.share permission. Cache keys carry the consumer project.
  • Audit: every publish, refusal and purge is on the cache’s Activity.
  • Alerts: FrazerCacheByteHitRateDrop (byte hit rate down 20 points in 15 minutes), FrazerCacheTierDown, FrazerCachePressure and FrazerCacheQuota90.
Setting Default
Namespace quota 200 GiB
Pull request overlay 2 GiB
Agent session overlay 5 GiB
Dependencies Evicted after 30 days unused
Build outputs Least recently used first; evicted after 14 days unused
Overlays Deleted 7 days after the pull request closes or the session ends
Warm disks Deleted after 7 idle days; 3 generations kept; clone chains flattened at depth 4
Node hot tier Evicts at 80 % disk use, aggressively at 90 %, stops admitting at 95 %
Slow or failing cache Treated as a miss within 2 s
Scope One site; a cache never spans sites and is never shared between organizations

On your own servers the cache uses your disks and is part of the per-core server fee. On the Frazer fleet you pay for stored GiB-months, with no request fee and no egress fee inside a site. Reads across sites are charged at the network rate, shown on the cache page.

CacheOn your serversOn the Frazer fleet
PriceIncluded · uses your disks$0.012 / GiB-month · no request fee

Every line, with the estimator: pricing.

Symptom Cause Fix
A job’s writes never show up for other jobs The job’s writer class may write only its overlay (pull requests, agents), nothing (forks), or the job did not finish with success. Check frazer cache writes --refused, and frazer cache policies test with the job’s identity. Shared entries come from protected-branch CI and prebuilds.
Main’s CI cannot write shared entries The branch is not protected, or the workflow is not on the policy’s protectedCi allow-list. Protect the branch, or add the workflow file to writers.protectedCi.workflows.
Misses right after a toolchain or image change Keys include the toolchain, the OS and architecture, and the runner image digest, so a hit must be exact. Expected. The next protected-branch run publishes new entries. Top misses on the cache page names the reason.
Jobs are slower and the console shows a degraded banner A cache tier is down or slow. Clients treat it as a miss within 2 s. Builds keep running. The banner shows the measured impact; FrazerCacheTierDown fires.
A job’s timeline shows a long warm-disk step The server holding the warm disk was full, so the job filled a fresh volume from the site’s store. Nothing to fix for one job. If it happens often, add servers to the pool.
Entries you need are evicted The namespace, project or site quota is full, or a server is under disk pressure. Raise the quota in the policy, pin what must stay, and watch FrazerCacheQuota90 and FrazerCachePressure.
A bad entry was published A trusted writer published it. frazer cache purge --writer <identity> --dry-run, then re-run the affected jobs the plan lists.

CLI

CommandWhat it does
frazer cache getShow a cache's tiers, usage and byte hit rate (bytes served ÷ requested).
frazer cache namespaces listList a cache's namespaces, filtered by tier and sorted by byte hit rate or size.
frazer cache policies getShow a cache policy; with -o yaml, its writers, scope, quotas and eviction rules.
frazer cache policies testShow what a given writer may read and write under a policy.
frazer cache volumes listList the warm disks of a repository.
frazer cache purgeRemove entries by writer identity or by namespace, as a plan that lists the affected jobs.
frazer cache writesList published and refused writes; with --refused, refusals only.

All of them: CLI reference.

MCP tools

ToolWhat it does
frazer.cache.v1Read caches, namespaces and policies, test what a writer may do, and plan a purge. Writes become a plan that waits for approval.

All of them: MCP tools reference.