Remote cache
Giant’s remote cache has two backends:
- The Bazel HTTP cache protocol -
the same protocol used by
bazel-remote, BuildBuddy, sccache, and various S3-backed servers. Run one anywhere, point Giant at it. - The GitHub Actions cache: when CI runs on Actions, Giant uses the runner’s own cache service directly - shared caching with zero infrastructure to host.
Enable the feature
Section titled “Enable the feature”The remote cache lives behind a feature flag so the default binary stays small and dependency-light:
cargo install --path crates/giant --features remotePre-built binaries from giant.build/install.sh include the remote feature.
Configure
Section titled “Configure”Remote cache settings live in the root giant.yaml only - a
workspace-global setting that a nested package can’t
carry.
# giant.yaml (workspace root)remote: enabled: true url: "https://cache.example.com" auth: kind: bearer token_env: GIANT_REMOTE_TOKENAuth shapes:
# No auth (open cache, e.g. a private network bazel-remote)remote: enabled: true url: "http://cache.internal:8080"
# Bearer token from env varremote: enabled: true url: "https://cache.example.com" auth: kind: bearer token_env: GIANT_REMOTE_TOKEN
# HTTP Basic authremote: enabled: true url: "https://cache.example.com" auth: kind: basic username_env: GIANT_REMOTE_USER password_env: GIANT_REMOTE_PASSThe GitHub Actions cache
Section titled “The GitHub Actions cache”Inside a GitHub Actions job, the runner already hosts a cache service -
the one actions/cache uses. Giant can speak to it directly:
# giant.yaml (workspace root)remote: enabled: true kind: github_actionsNo url, no auth - the runner provides both. The catch is that the
runner only exposes its credentials (ACTIONS_RESULTS_URL,
ACTIONS_RUNTIME_TOKEN) to JavaScript actions, never to plain run:
steps, so the workflow exports them once before any giant step (the
same dance sccache and BuildKit require):
- name: Expose the cache credentials to giant uses: actions/github-script@v7 with: script: | core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- run: giant build --quietThe config commits once and behaves sensibly everywhere: outside
Actions, a github_actions remote is simply inactive and builds run
with the local cache only. Inside Actions with the export step missing,
giant fails at startup with an error naming the variables - the one
case that’s a workflow bug rather than a normal environment.
Three GitHub-isms to know:
- Branch scoping. Entries written on the default branch are
readable from every branch; a PR’s writes are visible only to that
PR. Net effect:
mainwarms the cache, PRs read it, PRs can’t pollute each other. - Quota. GitHub gives each repo 10 GB, evicting least-recently-used
entries.
cache.max_size_gbdoesn’t apply here; GitHub does its own housekeeping. - Rate limits. Heavily parallel builds can get throttled. Giant’s remote is best-effort everywhere, so a throttled call degrades to a cache miss and the build carries on.
How the lookup chain works
Section titled “How the lookup chain works”When Giant computes a cache key, it tries sources in this order:
- Local AC lookup. Fastest - usually sub-millisecond.
- Remote AC lookup. Network round-trip; succeeds if any machine built this key before. The remote AC entry tells us which CAS blobs to pull.
- Remote CAS download. Pull each blob; verify hash; write to local AC + CAS for future fast-path.
existscheck (if declared on the target). External resource already there? Skip the build.- Local execution. Run the command. After success, write local AC + CAS, queue upload to remote.
The upload queue runs in the background - the build doesn’t wait for it before moving to the next target. Uploads are best-effort; a failure here doesn’t fail the build (you get a warning).
Bring up a bazel-remote server
Section titled “Bring up a bazel-remote server”docker run -d \ -p 8080:8080 \ -v /var/cache/bazel-remote:/data \ buchgr/bazel-remote:latest \ --dir /data --max_size 50That’s an open, unauthenticated cache. Point Giant at it:
remote: enabled: true url: "http://localhost:8080"For auth, see bazel-remote’s docs - Giant supports the bearer-token flow it offers.
Permissions in shared caches
Section titled “Permissions in shared caches”If two developers’ workspaces produce the same cache key, they’ll share the cached outputs. Giant doesn’t ship any sandboxing, so make sure your build commands are reproducible:
- Pin toolchain versions so every machine keys the same - see Pinning toolchains.
- Avoid embedding absolute paths in outputs.
- Don’t depend on the user’s
$HOMEor$USERunless they’re listed inenv:.
Test by running giant build on a different machine with an empty
local cache. If the remote cache hits and the outputs work, you’re
good.
Disabling remote uploads per target
Section titled “Disabling remote uploads per target”# cmd/server/giant.yaml → //cmd/server:server- name: "server" remote_cache: false # local cache onlyUseful when:
- Outputs are larger than is worth shipping over the wire.
- Outputs contain non-portable data (machine-specific paths).
- The build is so fast that the network round-trip would be slower.
cache: false opts out of caching entirely - local and remote both.
Inspecting cache behaviour
Section titled “Inspecting cache behaviour”$ giant build //cmd/server:server↓ REMOTE //cmd/server:server 120ms # downloaded from remote$ giant build //cmd/server:server✓ CACHE //cmd/server:server 2ms # local cache hitThe verb tells you which layer answered.
The stdout/stderr of the cached invocation is replayed automatically on any cache hit (local or remote) - see Log capture and replay.