GitHub Actions remote build cache setup
Connect Nx, Lerna, Turborepo, Gradle, or Bazel to Cachely from GitHub Actions. Add one secret to a trusted pipeline and share cached build outputs across runners and developer machines.
1. Choose your build tool
Create a workspace through the getting started guide, then choose Nx or Lerna, Turborepo, Gradle, or Bazel below. Your build tool talks to Cachely directly; no Cachely-specific GitHub Action is required.
Keep your existing dependency cache. Cachely reuses task outputs, while setup actions and actions/cache can restore package downloads. The GitHub Actions cache guide explains how to combine these layers and troubleshoot restore failures.
2. Add a Cachely secret
If you trust the users who can run or modify the pipeline and the code it builds, create one read-write Cachely token. In your repository, open Settings, Secrets and variables, Actions, and add CACHELY_TOKEN as a repository secret. Trusted pull-request builds can use the same token. You choose which builds may upload; the optional access section covers untrusted builds.
GitHub normally withholds Actions secrets from forked pull requests and Dependabot events. The recipes below build without Cachely when the secret is absent. For an ordinary trusted run, a missing token means remote caching is not configured. See GitHub's secret rules.
3. Nx and Lerna workflow
This example assumes an npm lockfile, a committed .nvmrc, and a cacheable Nx target named my-app. Replace the target with yours and follow Nx setup for supported versions and output configuration. Change main if your default branch differs.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions: {}
concurrency:
group: '${{ github.workflow }}-${{ github.ref }}'
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@v6.1.0
with:
persist-credentials: false
- uses: actions/setup-node@v6.5.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- name: Build with Nx
env:
CACHELY_TOKEN: '${{ secrets.CACHELY_TOKEN }}'
run: |
set +x
if [ -n "$CACHELY_TOKEN" ]; then
export NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://remote.cachely.dev
export NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN="$CACHELY_TOKEN"
fi
npx nx build my-appFor Lerna, replace the final command with npx lerna run build; Lerna uses Nx's cache. Keep your existing runtime versions, package manager, and build tasks when adapting an existing workflow. The examples assume no other remote-cache configuration; remove any previous provider configuration before using them.
These exports apply to commands in the same step. Repeat the cache environment in each build or test step that needs it, or combine those commands in one step. A dependency-cache hit from setup-node does not prove an Nx remote hit.
Turborepo in GitHub Actions
Keep the Node setup and install steps, then replace the Nx step with this one. Configure task outputs first using Turborepo setup. A configured token enables both remote reads and writes.
- name: Build with Turborepo
env:
CACHELY_TOKEN: '${{ secrets.CACHELY_TOKEN }}'
run: |
set +x
if [ -n "$CACHELY_TOKEN" ]; then
export TURBO_API=https://remote.cachely.dev
export TURBO_TOKEN="$CACHELY_TOKEN"
export TURBO_TEAM=cachely
fi
npx turbo run build --summarizeTURBO_TEAM only needs a non-empty value; Cachely identifies your workspace from the token.
Gradle and Android in GitHub Actions
Replace the Node setup and npm install with your existing JDK and Gradle setup. Android projects also need their SDK and compatible JDK. Add the Gradle HttpBuildCache configuration to your settings file, then use this build step:
- name: Build with Gradle
env:
GRADLE_CACHE_TOKEN: '${{ secrets.CACHELY_TOKEN }}'
GRADLE_CACHE_PUSH: 'true'
run: ./gradlew build --build-cacheThe linked settings disable the remote cache when the token is empty. Keep your project's wrapper and build tasks; no Cachely Gradle plugin is needed. If you use gradle/actions/setup-gradle, its caching can coexist with Cachely. See Gradle's GitHub Actions guide.
Bazel in GitHub Actions
Replace the Node setup and npm install with your existing pinned Bazel toolchain setup. Follow Bazel setup for cache configuration. This step keeps the authentication header in a temporary rc file and removes it when the shell exits:
- name: Build with Bazel
shell: bash
env:
CACHELY_TOKEN: '${{ secrets.CACHELY_TOKEN }}'
run: |
set +x
if [ -z "$CACHELY_TOKEN" ]; then
bazel build //... --remote_cache=
exit
fi
umask 077
cache_rc=$(mktemp)
trap 'rm -f "$cache_rc"' EXIT
printf 'build --remote_cache=https://remote.cachely.dev\nbuild --remote_header="Authorization=Bearer %s"\n' "$CACHELY_TOKEN" > "$cache_rc"
bazel --bazelrc="$cache_rc" build //...Do not print or upload the temporary file. Use an isolated runner for jobs that receive cache credentials.
Optional: limit writes for untrusted builds
Keep one read-write token when you trust the pipeline's users and build code. If some builds may read private outputs but should not upload, use a read-only token for those builds. If they should not read private outputs either, give them no token.
For repositories with untrusted contributors who can edit same-repository workflows, keep the write token out of repository and organization secrets available to those workflows. Store it only in a protected GitHub environment restricted to your trusted branch, and attach that environment only to the trusted job. A separate workflow file or an if condition alone does not prevent modified workflows from requesting a repository secret. Check GitHub's environment protection rules for availability and branch restrictions before choosing this setup.
In jobs that should read only, bind secrets.CACHELY_TOKEN_READONLY directly in place of secrets.CACHELY_TOKEN. Do not fall back to a write secret if the read-only secret is missing. Cachely enforces token scope even if code changes an upload setting:
- Nx and Lerna: the read-only token restricts uploads.
- Turborepo: also add
--cache=local:rw,remote:r. - Gradle: also set
GRADLE_CACHE_PUSHto'false'. - Bazel: also add
--remote_upload_local_results=falseto the build command.
Keep fork builds on pull_request without cache secrets. Do not use pull_request_target to execute untrusted code with tokens. Learn more in tokens and access control.
4. Verify a remote cache hit
Run a trusted build to populate Cachely, then repeat the same revision, dependencies, and tasks on a fresh runner using the same token. For this check, avoid restoring the tool's local task cache. Confirm reads for the CI token in the Cachely dashboard.
- Nx and Lerna: look for
[remote cache]and restored outputs. See Nx verification for clearing a local task cache. - Turborepo: inspect the
--summarizereport in.turbo/runsfor remote hits. - Gradle: temporarily set
local { isEnabled = false }insidebuildCachefor both runs and use./gradlew clean build --build-cache --info. Look forFROM-CACHEand matching dashboard reads, then restore local caching. - Bazel: look for
remote cache hiton a fresh runner without a disk cache.
No remote activity on a trusted build usually means the secret or environment did not reach the build step. A 401 points to the token; a 403 on upload can mean a read-only token was paired with uploads enabled. See cache troubleshooting.