3.1.10 Cloud Storage Dependencies

extra

When dependency bytes already live in cloud object storage, you do not have to force them through generic HTTP rules or shell out from a genrule. A storage-specific ruleset can keep the fetch inside Bazel's repository machinery, so the downloader, repository cache, and credential-helper flow still apply.1,2 A concrete provider-specific example is rules_gcs, so the examples below use GCS. The reusable maintainer pattern is "fetch through Bazel-aware repository logic, not ad hoc scripts."1,2,3 That keeps this topic separate from 3.1.9 Offline / Air-Gapped Builds, which is about portability and no-network operation, and from 6.2 Shared Remote Cache, which is about sharing build outputs rather than dependency sources.

Use cloud storage rules when the bucket is the source of truth

rules_gcs gives GCS-native versions of the familiar HTTP repository rules: gcs_file for a single file and gcs_archive for an archive Bazel should download and extract.1 They take gs://... URLs, translate them to HTTPS internally, and still go through Bazel's downloader instead of bypassing it.1 That matters because Bazel's repository-rule API reuses the repository cache when sha256 or integrity is provided, then stores the downloaded file there after a successful fetch.4

gcs_archive = use_repo_rule("@rules_gcs//gcs:repo_rules.bzl", "gcs_archive")
gcs_archive(
    name = "magic",
    url = "gs://my_org_code/libmagic.tar.gz",
    sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    build_file = "@//:magic.BUILD",
)

Use this pattern when the bucket is where the dependency is actually published: internal tool archives, test assets, or third-party blobs mirrored into your own storage.1 If what you really need is better resilience for ordinary registry or HTTP downloads, the right tools are still mirrors, registry policy, and downloader config from 3.1.6 BCR Infrastructure rather than a new storage-specific ruleset.

One object is simple. Many objects are where the pattern pays off

The interesting part is not one archive. It is a bucket with many objects where most builds touch only a small subset. rules_gcs handles that with the gcs_bucket module extension: a hub repository reads a lockfile, then Bazel creates per-object repositories and fetches them only when a build actually needs them.1 That directly contrasts with the "download everything up front" failure mode discussed in 3.1.5 Eager Fetch Anti-pattern.1

gcs_bucket = use_extension("@rules_gcs//gcs:extensions.bzl", "gcs_bucket")
gcs_bucket.from_file(
    name = "trainingdata",
    bucket = "my_org_assets",
    lockfile = "@//:gcs_lock.json",
)

gcs_bucket can expose those objects through symlinks, aliases, copies, or an eager mode, but the maintainer decision is simpler: use the bucket extension when you need many independently addressed objects and want network traffic to stay proportional to what the build actually used.1 If you want the mechanics underneath that design, 4.10 Authoring Module Extensions explains why extensions aggregate configuration and then call repository rules, while 4.9 Repository Rules covers the downloader-facing implementation model.

Private buckets need downloader-aware authentication

The fragile alternative is fetching bucket content outside Bazel, for example with wget inside a genrule. That approach falls apart for concrete reasons: Bazel cannot see the network edge for bazel fetch or bazel vendor, credentials end up in build logic, and you give up the downloader's normal caching behavior.2 Cloud-storage rules work better because they keep the fetch inside repository rules or module extensions, where Bazel can still reason about dependency downloads.1,2

For private GCS buckets, the concrete setup is a credential helper scoped to storage.googleapis.com in .bazelrc.1 Bazel's helper configuration also supports exact host matches, wildcard host matches, and a default fallback helper, including S3-style host patterns such as bucket.s3.us-east-1.amazonaws.com or *.s3.us-east-1.amazonaws.com.3 Put differently: keep authentication in 3.2.1 .bazelrc Hierarchy, not in BUILD snippets or shell wrappers.

# GCS
common --credential_helper=storage.googleapis.com=%workspace%/tools/credential-helper

# S3-style hostname scoping
common --credential_helper=*.s3.us-east-1.amazonaws.com=/path/to/helper/for/aws-s3

The upstream adoption path keeps three separate boundaries visible: examples/tweag-credential-helper/ shows the helper selecting a local credential identity and returning request headers.3,5 The helper does not grant object access: it supplies request credentials, and the storage service separately authorizes or rejects access. The examples/full/gcs_lock.json and its hash-check target independently verify that the downloaded bytes match the pinned content identity.5 Review and commit lockfile changes separately so that authorized access and approved, integrity-pinned bytes remain distinct.

The GCS example is the concrete one here, but the lesson generalizes cleanly: the storage backend may change, yet the Bazel-facing shape stays the same. Fetch through repository logic, scope credentials to the host that needs them, and keep hashes on the downloaded objects so the repository cache can do useful work.1,3,4

The S3 counterpart shows where backend-specific diagnosis begins. rules_s3 documents S3-compatible endpoint selection alongside separate routes for credential-helper failures, HTTP 401/403 responses, checksum mismatches, and empty downloads.6 Start with its troubleshooting guide before inspecting private signing or download code: authentication, endpoint routing, and content integrity fail at different boundaries.

key takeaway

Use cloud-storage dependency rules only when object storage is the thing you are depending on. gcs_file and gcs_archive cover single artifacts, gcs_bucket covers large object sets without eager fetches, and credential helpers keep private access out of BUILD logic. If the real problem is offline portability, go to 3.1.9 Offline / Air-Gapped Builds. If the real problem is sharing build outputs, go to 6.2 Shared Remote Cache.

Check your understanding · 3 questions

1.Why is it preferable to use gcs_archive instead of fetching GCS objects with wget inside a genrule?

Select one answer

2.True or false about the gcs_bucket module extension pattern:

Choose True or False for each sentence

gcs_bucket fetches all objects in the bucket eagerly when the extension first runs.
gcs_bucket creates per-object repositories and fetches them lazily when a build actually needs them.
Credential helpers for private GCS buckets should be embedded in BUILD files for security.

3.A team depends on objects from both GCS and S3 in their builds. Where should credential helpers be configured?

Select one answer

0 of 3 answered

Footnotes

  1. Introducing rules_gcsgcs_file, gcs_archive, gcs_bucket, gs:// URLs, private-bucket auth, lazy fetching, and repository-cache integration 1 2 3 4 5 6 7 8 9 10 11

  2. Fetching private data with Repo Rules and MODULE Extensions - Malte Poll, Modus Create — why genrule plus ad hoc download commands breaks Bazel's dependency workflow, and how repo rules keep fetching inside Bazel's downloader/cache model 1 2 3 4

  3. Configuring Bazel's Credential Helper — exact-match, wildcard, and default --credential_helper scopes, including S3-style hostname examples 1 2 3 4

  4. repository_ctxdownload() / download_and_extract() checksum behavior and repository-cache reuse 1 2

  5. rules_gcs — cloud-storage repository rules — the recommended credential-helper example and examples/full/ separate private access from lockfile-pinned object verification. 1 2

  6. rules_s3 — integrity-checked S3 dependencies — the public README separates endpoint, authentication, download, and checksum diagnostics from the private implementation.