Who this helps: teams running Bazel on a remote Mac where cross-border fetches and shared runners collide. You get: a bottleneck map for WORKSPACE and MODULE.bazel resolution, a disk versus remote cache threshold table, copy-paste flags (--disk_cache, --remote_cache, --repository_cache, --jobs), and Git shallow clone coordination notes. Pair with the blog index, cross-border mirror CI guide, and build pool disk FAQ—public pages, no login wall.
  • Registry fan-out: Bazel modules hit the Bazel Central Registry and GitHub tarballs; one flaky hop stalls analysis before your first compile action runs.
  • Cache collision: many jobs append to the same --disk_cache path while downloading into --repository_cache on one SSD—latency spikes look like “slow Bazel” but are queue depth.
  • Git depth mismatch: CI uses git clone --depth 1 for the app repo while a git_repository or vendor script expects tags outside the shallow window.

① Bazel externals and registry bottlenecks on a remote Mac

On Apple Silicon CI hosts, the expensive phase is rarely the first compiler invocation—it is repository resolution. WORKSPACE-style rules (http_archive, git_repository, custom macros) download archives and Git objects over HTTPS. Bzlmod paths resolve through the module registry, which still ends on tarball and Git providers. Cross-border links add RTT variance, middlebox TLS inspection, and occasional reset-by-peer bursts that surface as “repository failed” rather than compile errors.

Disk enters the picture twice: the repository cache stores immutable downloads keyed by content, while extracted trees land under $(bazel info output_base)/external. If output_base sits on the same volume as a hot --disk_cache, parallel analyses from multiple CI jobs interleave random writes from action outputs with sequential-ish unpack patterns from externals. Treat network and IO as one system: when registry latency is good but builds jitter, inspect per-volume throughput before raising --jobs.

Related: Git and Docker pull acceleration for transport-layer patterns that also help Bazel’s HTTP fetches, and remote Mac build node guide for SSH-first layout of workspaces and caches.

② Local disk_cache versus remote_cache: selection thresholds

Use the table before you buy hardware or enable a shared endpoint. --disk_cache stores action output blobs locally; --remote_cache (gRPC or HTTP) shares action outputs across machines. --repository_cache is the third leg—it deduplicates upstream downloads and is usually the highest ROI for cross-border CI.

Signal you measure Prefer local --disk_cache Prefer --remote_cache Notes
Number of concurrent CI jobs on one host 1–2 jobs; predictable reuse window 3+ jobs or ephemeral workspaces wiped each run Remote cache shifts action output reuse off local SSD; still keep --repository_cache local when egress is costly
Hit rate after cold bazel clean --expunge High when the same user or pool rebuilds one mono-repo daily High when many repos share toolchains and genrules Namespace caches per trust boundary; do not mix regulated artifacts with public repos without policy review
Egress cost / cross-border stability Acceptable when mirrors sit near the runner region Strong when the cache endpoint is co-located with runners A remote cache in another continent can hurt more than it helps—tune --remote_timeout and download modes
Operational burden Low; path plus janitor script Higher; TLS, auth headers, quotas Hybrid pattern: --repository_cache + --disk_cache locally, --remote_cache for action outputs only

Concurrency knob: --jobs=N scales local actions. On fetch-heavy builds, high N before the repository cache warms increases simultaneous unpacks and competes with disk cache writers—start conservative, watch IO wait, then step up.

③ WORKSPACE / MODULE fetch retries, timeouts, and cache flags

Pin these in checked-in .bazelrc or generated CI rc fragments. Adjust paths and secrets; never paste live tokens into public repos—inject --remote_header values from the orchestrator.

Flag or setting Purpose Example / starting point
common --repository_cache=… Reuse downloaded externals across workspaces /usr/local/ci/bazel-repo-cache on NVMe
build --disk_cache=… Local action output cache /usr/local/ci/bazel-disk-cache per pool
build --remote_cache=… Shared action cache (gRPC/HTTP) grpcs://cache.example:443
build --remote_header Auth for remote cache / proxy Authorization=Bearer $(CACHE_TOKEN) via env expansion in CI
build --remote_timeout Longer uploads on slow uplinks 600 seconds as a first trial for large blobs
common --experimental_repository_downloader_retries Retry flaky repository HTTP downloads 5 after validating Bazel release notes for your minor version
build --jobs Local action parallelism Half of performance cores when disk and repo cache share one volume; raise when IO wait stays low
MODULE mirrors (enterprise) Override default registry URLs --registry=file:///… or mirrored index—keep in sync with security review

Illustrative fragment—split across profiles in real pipelines:

# Shared CI prelude (paths are examples)
common --repository_cache=/usr/local/ci/bazel-repo-cache
common --experimental_repository_downloader_retries=5
build --disk_cache=/usr/local/ci/bazel-disk-cache
build --remote_cache=grpcs://bazel-cache.internal.example:443
build --remote_timeout=600
# build --remote_header=Authorization=Bearer ${BAZEL_CACHE_TOKEN}

Validate with a single warm-up job: bazel fetch //… or an analysis-only target, then enable matrix fan-out. Surface failures in CI logs with --verbose_failures during the first rollout week.

④ Git partial clones, directories, and IO quotas (FAQ)

Should the app repo use shallow clone while Bazel pulls nested Git dependencies?

Yes for the checked-out project when commit SHAs are fully reachable at the declared depth. Keep Bazel’s external Git roots on immutable SHAs in WORKSPACE or module patches. If a rule needs tags outside the shallow set, increase depth for that clone step or vendor the archive with http_archive and checksum.

Can --repository_cache share a volume with --disk_cache?

Physically yes, but budget IOPS. Splitting across two fast volumes reduces tail latency when analysis, fetch, and compile overlap. Leave at least 15–20% free space on each cache volume so APFS does not throttle during CI peaks.

What breaks when five workflows hit one shared --disk_cache folder?

Read-heavy phases are usually safe; pruning jobs or aggressive cleaners racing writers cause sporadic corruption messages. Prefer remote cache for multi-tenant hosts or use per-pool subdirectories and serialized maintenance windows.

How do I separate “network flake” from “disk saturated”?

Align timestamps from Bazel’s repository log with host disk metrics. High queue depth with normal ping to the registry implicates disk; idle disk with TLS handshake stalls implicates egress or DNS—match fixes to SSH relay and proxy patterns where applicable.

⑤ Remote Mac region and parallel compile impact on fetch stability

Place the runner close to the registries and mirrors you actually use. A remote Mac in region A with a remote cache in region B may save compile time yet lose those gains to upload/download phases, especially for large intermediate artifacts. Co-locate the cache endpoint, artifact storage, and runner when cross-border links are lossy.

Parallel compiles amplify the issue: high --jobs increases local CPU and disk pressure while repository rules may still stream downloads. Stagger matrix legs, cap simultaneous Bazel invocations per host, or dedicate one “fetch” job that populates --repository_cache before fan-out. This pattern mirrors the queueing advice in the concurrent pull and disk FAQ—treat Bazel as another heavy consumer of the same IO budget.

Rollout checklist: five executable steps

1
Freeze Bazel and module policy. Record the Bazel LTS version, whether Bzlmod is enabled, and which registry mirrors are approved.
2
Create cache paths. mkdir -p for repository and disk caches; set owner to the CI user; exclude them from Time Machine if backups slow disks.
3
Commit rc snippets. Add common lines for --repository_cache and retries; add build lines for --disk_cache and optional --remote_cache with timeouts.
4
Tune --jobs. Benchmark one representative target at N = cores/2, then raise while monitoring disk latency.
5
Align Git checkout scripts. Document depth for the main repo versus Bazel externals; add a preflight bazel fetch step on fresh images.

Summary

Bazel on remote Mac CI fails loudly in the fetch layer: tune --repository_cache first for cross-border stability, add --disk_cache or --remote_cache based on host count and reuse, and cap --jobs against real disk throughput. Pair immutable Git SHAs with shallow clones consciously, and keep cache endpoints in the same region as your runners when WAN is the bottleneck.

When you are ready to offload fetch-heavy builds to dedicated Apple Silicon, start at the homepage, compare pricing, and complete purchase or rental in a few clicks. Use the help center for provisioning questions, browse the technical blog for more CI playbooks, and revisit remote Mac build node setup for SSH-oriented workflows—no login required to read these pages.

Next reads: Gradle and Maven cache matrix for polyglot repos, and Go GOPROXY matrix when services mix languages under one pipeline.

Remote Mac for Bazel CI and cache-heavy builds

Dedicated Apple Silicon nodes, fast SSD layouts for repository and disk caches, and SSH-friendly debugging. Open pricing, purchase, help, or the blog without signing in.