self-host →

Magic Cache

Magic Cache swaps the GitHub Actions cache backend for a fast, unlimited S3 cache in your own VPC. Same actions/cache config, nothing leaves your account.

RunsOn Magic Cache transparently switches the GitHub Actions caching backend to a fast, unlimited S3 cache in your own VPC — so you keep using actions/cache (and language-specific cache actions) completely unchanged.

magic-cache · request path
github actions cache internet · 10 gb cap · rate-limited ✕ the slow path — never taken // your aws account · vpc ec2 runner · ephemeral localhost s3 gateway · in-vpc · free actions/cache your workflow step — unchanged runs-on sidecar :cache backend · started by agent s3 cache bucket unlimited · 10-day evict key
01/05 actions/cache sidecar GET npm-linux-x64-… · over localhost
up to faster
than the official GitHub Actions cache
unlimited
no 10 gb cap — evicts on your policy
$0 transfer
ingress/egress stays inside your vpc

Platform availability#

Cache backendLinuxWindowsFlexFleet
Magic Cache for actions/cacheYesYesYesYes
Docker Buildx type=ghaYesNoYesYes
Docker Buildx type=s3YesNoYesYes
Ephemeral ECR registryYesNoYesNo

How it works#

With the Magic Cache enabled, the RunsOn agent starts a sidecar cache backend on the runner and points the caching actions — including the gha backend for Docker Buildx — at it.

The sidecar transparently forwards every request to an S3 cache bucket in your own VPC and streams hits straight back. Same actions/cache config, nothing leaves your account, and there’s no 10 GB cap.

Because only the storage backend changes, the exact same workflow keeps working when you fall back to GitHub-hosted runners — the cache just points at GitHub’s backend instead.

If the sidecar fails to start (5s timeout), the job falls back to GitHub’s standard cache backend and emits a warning annotation — the job is not failed.

Enabling it#

The Magic Cache is opt-in. Flip it on per job (Flex) or bake it into a runner fleet (Fleet):

  • Flex — add the extras=s3-cache job label and the runs-on/action@v2 step. The choice lives with the workflow author.
  • Fleet — publish cache-enabled runners from Terraform (extras = ["s3-cache"]). The choice lives with the platform team.
AspectFlexFleet
Who choosesWorkflow author, per jobPlatform team, in Terraform
Where it livesextras=… label or .github/runs-on.ymlrunners.<name>.extras in Terraform
Magic Cache (S3)
Docker BuildKit S3
Ephemeral registry

The detailed guides below have copy-paste examples for both models.

Retention#

S3 Lifecycle calculates expiration by adding a fixed lifecycle window (default 10 days) and rounding up to the next midnight UTC. Restoring an object does not extend that window. For Fleet and Flex, the Terraform module provisions the bucket and exposes the window:

cache_expiration_days = 10

Use a longer window for slow-changing dependency caches, and a shorter one for high-churn branches or very large artifacts.

Scope and isolation#

By default, Magic Cache keeps the stack-wide access model used by earlier v3 releases. Cache isolation scopes the Magic Cache protocol, but it is not a trust boundary for untrusted job code. Run public or otherwise untrusted repositories in a separate RunsOn stack from trusted workloads.

Isolate GitHub Actions caches#

v3.2 adds opt-in, brokered credentials for Magic Cache. They scope actions/cache, language setup actions, and the Buildx type=gha backend to the current repository and branch.

DeploymentSetting
Flex CloudFormationEnableCacheIsolation: "true"
Flex Terraform / OpenTofuenable_cache_isolation = true
Fleet Terraform / OpenTofuenable_cache_isolation = true

Isolation stores Magic Cache data under scoped-cache/* instead of cache/*. Expect one cold cache per repository and branch after enabling it. Existing objects keep their normal retention and expire without migration.

Fleet capacity-backed runners also get one cold classic Magic Cache after a v3.2 upgrade, even if isolation remains off. v3.2 corrects their old empty repository prefix and repopulates the matching cache/v1/<owner>/<repo>/... namespace automatically.

The separate v3.2 upgrade guide covers a safe rollout and the sticky-disk isolation that removes legacy snapshot permissions.

Detailed guides#

Other ways to cache#

The Magic Cache covers actions/cache and Docker layers. For caches tied to the runner’s disk rather than an S3 backend, see the runner capabilities:

  • Local storage (NVMe) — fastest temporary workspace and Docker storage when the instance type includes local NVMe. Data does not survive the runner.
  • Sticky disks (EBS) — fastest for large Docker layer sets (skips export and compression), but adds EBS storage cost and is less portable.
  • Shared volumes (EFS) — large shared datasets, monorepo mirrors, or dependency stores that should behave like a mounted disk.
  • YOLO mode (tmpfs) — hot temporary directories that fit in memory. Not for caches that must survive the runner.
  • Custom images — better than a cache when the input changes slowly. Bake the SDK or base layer into the AMI.