Skip to content

Your GitHub Actions cache,
on your own hardware.

A drop-in replacement for GitHub's cache service, written in Rust. actions/cache, setup-* caches and BuildKit type=gha work unchanged on self-hosted and ephemeral runners.

Get startedGitHub★ 460

Ephemeral runners

Cache server

Your storage

  • runner-7f3krunning job
  • runner-2c9xrunning job
  • runner-k41mrunning job
  • unknown clientno valid token
github-actions-cache-server
  1. verify runner tokenOIDC
  2. scope to repo and branchisolated
  3. look up key34k/s, p99 7 ms
  4. save2.3 GiB/s
  5. restore7.9 GiB/s

29 MB image0.5 s start3 MiB idle

  • local NVMe
  • S3, RustFS, MinIO, R2
  • Google Cloud Storage
  • Azure Blob
Restoring a 1 GiB cache62× faster than GitHub's cache from a self-hosted runner, on a 10 GbE network
  1. GitHub cache, self-hosted runner~20 MB/s reported54 s
  2. GitHub cache, GitHub-hosted runner~231 MB/s reported4.6 s
  3. This server, over 10 GbEline rate of the link0.86 s
  4. This server, same host7.9 GiB/s measured0.13 s

GitHub figures as reported by actuated (2023) and in google/wireit#239 (2022), your link and region decide the real number. Our method.

Two steps. Your workflows stay as they are.

  1. 1Run the server

    docker-compose.yml
    services:
    cache-server:
    image: ghcr.io/falcondev-oss/github-actions-cache-server
    ports: ['3000:3000']
    environment:
    API_BASE_URL: http://cache-server:3000
    volumes: ['cache-data:/app/.data']
    volumes:
    cache-data:
  2. 2Point your runners at it

    runner
    # your runner, ARC scale set or container
    image: ghcr.io/falcondev-oss/actions-runner
    env:
    CUSTOM_ACTIONS_RESULTS_URL: http://cache-server:3000/

    The forked runner keeps your cache URL and skips self-update. Prefer the stock image? Use the one-line patch.

  3. 3There is no step 3

    .github/workflows/ci.yml (unchanged)
    - uses: actions/setup-node@v6
    with:
    cache: npm
    - uses: actions/cache@v4
    with:
    path: ~/.cargo
    key: cargo-${{ hashFiles('Cargo.lock') }}

Why not a mounted volume?

A hostPath only helps tools you point at it, on one node, shared by every repository. The cache server speaks GitHub's protocol instead.

Full comparison →
GitHub cachehostPathThis server
Cache storedGitHub cloudone nodeyour server
Workflow changesnoneevery workflownone
actions/cache, setup-*yesnoyes
Ephemeral runnersyessame node onlyyes
Restore speed~20 MB/sdiskLAN line rate
Per-repo isolationyesnoyes

Works with what you already run.

Anything built on @actions/cache with the v2 cache service.

Actions
actions/cacheactions/setup-nodeactions/setup-pythonactions/setup-goactions/setup-javadocker buildx type=ghaSwatinem/rust-cacheany @actions/cache tool
Storage
FilesystemS3, RustFS, MinIO, R2Google Cloud StorageAzure Blob
Database
SQLitePostgreSQLMySQL
Runs on
DockerHelmARCamd64, arm64GHES
Operations
Prometheus /metricsLRU evictionself-healing cleanupmanagement API

Let your agent set it up.

Every page is plain Markdown for agents. Append .md to any URL, or send Accept: text/markdown.

Read https://gha-cache-server.falcondev.io/llms.txt and set up the GitHub Actions Cache Server for my self-hosted runners.

Ship it in two minutes.

MIT licensed. Built by falconDev.