CircleCI build cache setup with Cachely
Connect Nx, Lerna, Turborepo, Gradle, or Bazel to Cachely from CircleCI. Add a token to a context and share cached build outputs across jobs and developer machines.
1. Choose your build tool
Create a Cachely workspace using the getting started guide. CircleCI runs your build; the build tool connects to Cachely using its own remote-cache protocol. Choose the recipe for Nx or Lerna, Turborepo, Gradle, or Bazel below.
Keep your existing dependency cache. CircleCI's restore_cache and save_cache can restore downloaded dependencies; Cachely stores cacheable task outputs so Gradle can skip work when inputs match. See Gradle's CircleCI guide for the two layers.
2. Add your token to a context
For a pipeline whose users and build code you trust, create one read-write workspace token. In CircleCI Organization Settings, create a context named cachely and add the token as CACHELY_TOKEN. This lets each build read and populate the cache, including trusted pull-request builds.
You choose which builds may write. If users you do not trust can trigger the pipeline or change the code it runs, use the optional read-only setup for those builds.
3. Add the CircleCI workflow
The workflow below connects the context to a Gradle job. For another tool, replace the run step with its recipe below and use an executor containing that tool's runtime. Node recipes need your pinned Node version; Bazel needs your pinned Bazel version and toolchain. Keep your existing install and checkout steps.
# .circleci/config.yml
version: 2.1
jobs:
cache-build:
docker:
- image: cimg/openjdk:21.0.11
steps:
- checkout
- run:
name: Build with the remote cache
command: |
set +x
test -n "$CACHELY_TOKEN"
export GRADLE_CACHE_TOKEN="$CACHELY_TOKEN"
export GRADLE_CACHE_PUSH=true
./gradlew build --build-cache
workflows:
build:
jobs:
- cache-build:
context: cachelyContext variables are injected into the job environment. Each recipe maps CACHELY_TOKEN to the build tool's settings. Export inside the shell command, where variable expansion works.
Nx and Lerna on CircleCI
Use a Node executor and install from your lockfile. This npm example requires a cacheable build target named my-app; replace it with your target. Follow Nx setup for version requirements and cacheable outputs.
- run:
name: Build with Nx
command: |
set +x
npm ci
export NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://remote.cachely.dev
export NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN="$CACHELY_TOKEN"
npx nx build my-appFor Lerna, use the same exports and replace the final command with npx lerna run build. Lerna uses Nx's cache and the same token.
Turborepo on CircleCI
On your Node executor, use the Turborepo setup and declare task outputs in turbo.json. This step enables remote reads and writes:
- run:
name: Build with Turborepo
command: |
set +x
npm ci
export TURBO_API=https://remote.cachely.dev
export TURBO_TOKEN="$CACHELY_TOKEN"
export TURBO_TEAM=cachely
npx turbo run build --summarizeTurborepo's cache option controls client reads and writes if you want to change the default.
Gradle on CircleCI
Add the Gradle HttpBuildCache settings before using the workflow above. Its run step sets GRADLE_CACHE_TOKEN and enables uploads with GRADLE_CACHE_PUSH=true. Use the committed Gradle Wrapper and a compatible JDK. For Android, keep your SDK setup and Android tasks. No Cachely Gradle plugin is needed.
Bazel on CircleCI
Use your existing Bazel executor with the project's pinned version and toolchain. This step supplies the HTTP cache URL and token directly; do not commit a credential-bearing user.bazelrc. See Bazel setup.
- run:
name: Build with Bazel
command: |
set +x
bazel build //... \
--remote_cache=https://remote.cachely.dev \
--remote_header="Authorization=Bearer $CACHELY_TOKEN"The header carries the secret, so keep shell tracing off and restrict access to agents and build logs. Bazel's remote-cache documentation covers reads and uploads.
Optional: limit writes for untrusted builds
Keep the single-token setup when you trust everyone who can run or modify the pipeline and the code it builds. If some builds should only download cached outputs, give those builds a separate context containing a read-only token. Keep the read-write context available only to builds you trust.
- Nx and Lerna: use the read-only token; Cachely enforces its permissions.
- Turborepo: also add
--cache=local:rw,remote:rto the build command. - Gradle: also set
GRADLE_CACHE_PUSH=false. - Bazel: also add
--remote_upload_local_results=false.
Enforce access with CircleCI's context restrictions. Branch filters and upload flags can be changed in repository code, so they do not protect a read-write token on their own. A read-only token still allows downloads; if a build should not access private outputs, give it no Cachely context or remote-cache configuration.
4. Verify a remote cache hit
Run a build to populate the cache, then repeat the same revision, dependencies, and tasks on a fresh runner using the same context. Do not restore task outputs or the tool's local build cache for this check.
- Nx and Lerna: look for
[remote cache]and restored outputs. If reusing a runner, follow the Nx verification steps to clear its local task cache. - Turborepo: inspect the
--summarizereport under.turbo/runsfor remote hits. - Bazel: look for
remote cache hitin the action summary on a fresh agent without a disk cache.
For Gradle, temporarily disable the local build cache inside your existing buildCache block. Use this same configuration for both runs:
// settings.gradle.kts, inside buildCache { ... }
local { isEnabled = false }Run the job once to populate the cache, then repeat the same revision and tasks on a fresh CircleCI runner without restoring task outputs. Keep the Gradle exports from the workflow and use:
./gradlew clean build --build-cache --infoLook for FROM-CACHE on cacheable tasks and matching reads in the Cachely dashboard. UP-TO-DATE means existing outputs were reused; FROM-CACHE alone cannot distinguish local from remote unless the local cache is disabled. Restore the local-cache setting after verification.
An unauthorized context means its access restrictions did not pass. A 401 from Cachely points to the token; a 403 on upload usually means a read-only token was paired with uploads enabled. For other misses, see cache troubleshooting.
Connect another pipeline
Running multiple providers? Use the Jenkins setup guide or return to CI setup. Each pipeline can have its own token in the same Cachely workspace.