Skip to content

Caching

Every runner gets a persistent /cache volume. It survives across runner generations: a fresh ephemeral container picks up the warm cache the previous one left behind. A GitHub-hosted runner has nothing persistent attached, so this is where workflows for Runaway diverge.

Cache volumes belong to the host, not the workload. A workload selects one with an optional cache key.

Runaway points yarn, npm, pnpm, pip, Go modules, and cargo at the /cache volume with no configuration. Installs land on the persistent volume and the next run reuses them.

Skip GitHub’s Actions Cache for tool binaries

Section titled “Skip GitHub’s Actions Cache for tool binaries”

On a GitHub-hosted runner, actions/cache@v4 and actions/setup-node’s cache: input make sense: the runner is thrown away with nothing persistent attached, so caching means tar + compress + round-trip through GitHub’s Cache service.

On a Runaway runner that round-trip is slower than reading the already-warm local /cache volume. Point your tools at /cache instead.

Drop the cache: input — the pnpm store already lives on the volume:

# No `cache: pnpm` — the store already persists on /cache.
- uses: actions/setup-node@v4
with:
node-version: 22

Put browser binaries and other heavy tool downloads on the volume directly:

env:
PLAYWRIGHT_BROWSERS_PATH: /cache/ms-playwright

With the path on /cache, playwright install no-ops on warm runs instead of re-downloading browsers every job.

The effective /cache volume is resolved per host, org, and cache key:

  • No key — one shared cache per organization on that host, named runaway-cache.<org-slug>.
  • A key — a separate warm cache, named runaway-cache.<org-slug>.<key>.

Same-org workloads that use the same key share a warm cache on a host. Different organizations never share a cache — the volume is org-scoped, so private packages can’t leak across orgs.

Cache volumes are managed from the Cache tab on a host’s detail page/cache is a physically distinct Docker volume on each host, so each host shows its own volumes, never a cross-host sum. Inspect a volume’s size, see the per-ecosystem breakdown, and purge from there. The purge action is gated on no live runners using the volume, so you can’t wipe a cache out from under a running job.

A workload on a nested isolated runtime gets a fresh inner Docker daemon per runner, so Docker layer cache does not carry across generations — that matches github-hosted semantics. The /cache filesystem volume still bind-mounts in, so your ecosystem and tool caches survive exactly as they do on the shared-daemon runtime.