Skip to content
SYS.DOCS // DOCS

GitHub Actions Cache

The Actions cache keeps actions/cache and setup-* caches inside your cluster. Runner pods talk to a cache server next to them instead of GitHub’s cache backend, so restores and saves stay on the cluster network, and the cache size is bounded by the volume you give it rather than GitHub’s per-repository limit.

Workflows need no changes. Any step that uses the Actions cache protocol, such as actions/cache, actions/setup-node with cache, or actions/setup-go, uses the in-cluster cache as soon as it runs on a runner from this cluster.

The cache server is the open-source github-actions-cache-server. There is one cache per cluster, shared by every runner scale set on it.

  • The GitHub Actions Runner Controller installed on the cluster. See GitHub Actions runners.
  • A storage class that can provision a ReadWriteOnce volume. The cluster default is used unless you pick one.
  • The VictoriaMetrics app, if you want hit rate and storage statistics on the Cache tab.
  1. Open Cluster > Actions > Cache.
  2. Turn on the Actions cache switch.
  3. Set the storage size, retention, and placement.
  4. Select Save.
FieldDefaultNotes
Storage20GiSize of the cache volume, as a Kubernetes quantity.
Storage classcluster defaultFixed while the cache volume exists.
Keep unused entries (days)7Entries that were not restored for this many days are removed. 1 to 365.
Node poolanyPins the cache server to a node pool, for example metal servers with local NVMe disks.
Apply taint tolerationsonLets the cache server schedule onto a tainted node pool.

Saving starts the cache server in the controller namespace and re-applies every configured scale set so runner pods use the cache from their next job. If a scale set could not be re-applied, the save reports which one; open its Settings tab for the error.

  • Storage can be raised later if the storage class supports volume expansion. Kubernetes does not shrink volumes, so a smaller size is rejected.
  • Storage class is fixed once the volume exists. To move the cache to another class, uninstall the controller add-on, which removes the volume, and set the cache up again.
  • Node pool is fixed once the volume is bound. Local and topology-aware volumes can only be mounted from the nodes they were created on, so moving the server to another pool would leave it unschedulable. Move it back to the original pool, or uninstall the controller add-on to start over.

When the volume is more than 80% full, the Cache tab shows a warning. The server evicts the least recently used entries above 90%, so jobs keep working; raise Storage to keep more.

With the cache on, the Cache tab shows the last 24 hours:

  • Hit rate: restores that found an entry, with the hit and miss counts.
  • Uploads: caches saved by jobs.
  • Stored: bytes on the volume and how full it is.
  • Server: ready replicas and the server version.

The statistics come from VictoriaMetrics. Without the app, the tiles show a dash and the reason.

The Entries table lists what the server holds: repository, cache key, branch, size, when it was saved, and when it was last restored. Entries appear after the first cached job.

  • Delete removes one entry.
  • Clear all removes every entry on the cluster. Jobs keep working and rebuild their caches on the next run.

Nothing changes in the workflow. This job restores and saves its pnpm store through the in-cluster cache because it runs on a runner from the cluster:

jobs:
test:
runs-on: default
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm test

Custom runner images must keep the stock /home/runner layout of the actions/runner image. A runner that does not can still run jobs, but its jobs use GitHub’s cache backend.

Turn off the Actions cache switch and select Save. Runner pods go back to GitHub’s cache backend from their next job. The cache volume and its entries are kept, so turning the cache on again restores them.

Uninstalling the controller add-on removes the cache server and its volume.

The cache is shared by every scale set on the cluster. Only repositories you trust to write to a shared CI cache should run on runners that use it. Keep the same scoping rules as for the runners: limit the GitHub integration to the repositories that need private runners, and do not run untrusted pull request workflows on them.