4.9.10 Remote Downloader
extraRemote downloader is the infrastructure-heavy version of Bazel's normal archive download path. Instead of letting every Bazel client fetch a URL directly, the client can ask a gRPC Remote Asset service to fetch the blob, place it in a remote CAS, and then read it back from that cache.1 That makes it relevant only for controlled environments: air-gapped CI, restricted egress, corporate artifact proxies, or very large fleets that want dependency downloads to behave like shared infrastructure rather than per-machine network traffic.
No curl bypass or custom fetch loop.
The repo rule API stays unchanged.
Backends can change behind the service.
Bazel reads the blob and continues.
--downloader_config rewrites, allows, or blocks URLs around this path. It is not the gRPC Remote Asset service boundary.
Where It Fits
Repository rules usually fetch files through the Bazel downloader with repository_ctx.download() and repository_ctx.download_and_extract(). Module extensions have the corresponding module_ctx.download() and module_ctx.download_and_extract() methods.2,3 Those APIs are the same foundation discussed in 4.9.2 Archive Downloads & Integrity: the rule or extension supplies URLs plus an expected sha256 or integrity, and Bazel can check the repository cache before touching the network.4
Remote downloader adds another layer behind that downloader. A normal repository rule still says "download this URL with this checksum." The policy decision is outside the rule implementation: the Bazel client is configured to route the download request to a service that understands the Remote Asset API.1 In Stripe's production setup, the client issues a FetchBlob request with the URL, the service pulls from Artifactory if needed, writes the blob to CAS, and returns metadata so the client can read the blob from the remote cache.1
That boundary matters for rule authors. A well-written repo rule should continue to use download() / download_and_extract() with hashes, headers, and auth where appropriate. It should not shell out to curl, because that bypasses Bazel's downloader, repository cache, credential helpers, downloader config, and any remote downloader integration layered underneath.5
Remote Downloader vs Downloader Config
--downloader_config and remote downloader solve adjacent but different problems. Downloader config rewrites, allows, or blocks URLs before Bazel fetches them. Teams use it to redirect public URLs to internal mirrors, block unauthorized hosts, or validate that an offline CI job does not reach the public internet.6 It is a URL policy file for the Bazel downloader.
Remote downloader is a service boundary. The client sends a gRPC request to a remote asset service, and that service performs or serves the fetch.1 That can hide backend storage details from users, let infrastructure switch from one artifact source to another, and make upstream outages less visible to developers when the requested blobs are already cached or mirrored.1
Use downloader config when your main need is "rewrite this URL to our mirror" or "block everything except these hosts." Consider remote downloader only when you have infrastructure that can operate a Remote Asset service and a remote cache/CAS, and when centralizing dependency downloads is worth that operational cost.
Why It Helps Restricted Builds
Air-gapped and restricted builds need two properties: no surprise public-network access, and a reproducible way to pre-seed or mirror every external artifact. The normal toolbox for reducing or eliminating Internet dependence is bazel fetch, --nofetch, registry mirrors, --repository_cache, --downloader_config, and --module_mirrors.7
Remote downloader sits beyond that toolbox. It can turn "every developer or CI worker needs the right cache contents" into "the fleet asks one controlled service for blobs." The service can be backed by Artifactory, S3, a pre-seeded CAS, or another internal store. Clients do not need to know where the bytes came from.1
This does not make repository rules run remotely. Repository rules still execute locally during loading rather than on a remote executor. Module extensions also run in Bazel, not on a remote executor.8 Remote downloader centralizes file fetching. It does not offload arbitrary repository rule logic.
Version-Sensitive Flag Names
Remote downloader is experimental, and production setups cite --experimental_remote_downloader.1 Treat that as version-sensitive configuration, not stable API surface. Before documenting a company-wide setup, check the command-line reference for the Bazel version you run and keep any flags in a preset or shared .bazelrc layer rather than scattering them across repos.
Design Guidance for Rule Authors
Remote downloader works best when rules stay boring:
def _repo_impl(rctx):
rctx.download_and_extract(
url = rctx.attr.urls,
integrity = rctx.attr.integrity,
strip_prefix = rctx.attr.strip_prefix,
)
The contract is what matters. The repo rule should express the desired artifact, its checksum, and any request metadata through Bazel's downloader APIs. Omitting sha256 or integrity is a security risk and, at best, makes the build non-hermetic. When a checksum is present, Bazel can check the repository cache first and only download if the file is missing.4
Custom headers and auth should also go through the downloader path. The custom headers work that landed around Bazel 7.1 was motivated by rulesets that had fallen back to curl for Docker registry and Alpine package edge cases. Moving those requests back into repository_ctx.download() restored access to Bazel's caching downloader behavior.9
Remote downloader is not a new kind of repository rule. It is an infrastructure backend for the same hash-verified downloader path that good repository rules already use. If your rule uses Bazel's downloader APIs cleanly, a platform team can add mirrors, credential helpers, downloader config, repository caches, or a remote asset service without changing the rule's public API.
Check your understanding · 3 questions
1.What does a remote downloader change for a well-written repository rule?
Select one answer
2.Which statements describe the distinction between remote downloader and downloader config?
Select all that apply
3.True or false: remote downloader compatibility.
Choose True or False for each sentence
Footnotes
-
Remote execution, the DIY edition — Stripe Remote Asset API flow with
FetchBlob, Artifactory, CAS, and--experimental_remote_downloader. ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
repository_ctx — repository-rule context and
download()/download_and_extract()API. ↩ -
module_ctx — module-extension context with corresponding download APIs. ↩
-
repository_ctx — checksum and repository cache behavior for downloads. ↩1 ↩2
-
Fetching private data with Repo Rules and MODULE Extensions - Malte Poll, Modus Create — recommendation to use repository rules and the Bazel downloader rather than ad-hoc fetch commands. ↩
-
Fetching private data with Repo Rules and MODULE Extensions - Malte Poll, Modus Create — downloader config URL rewriting, blocking, and proxying use cases. ↩
-
Frequently asked questions — offline and Internet-insulation guidance for
bazel fetch,--nofetch, repository cache, mirrors, downloader config, and module mirrors. ↩ -
Frequently asked questions — why repo rules still run locally with remote execution. ↩
-
rctx.download custom headers coming to Bazel 7.1 — headers support and the value of staying on Bazel's downloader path. ↩