Azure Blob Storage
Back the cache with an Azure Blob container: account and container naming, the endpoint override for Azurite, and when a SAS URL replaces everything else.
Who this is for: anyone running NativeLink on Azure who wants the durable tier to be a Blob Storage container. What you'll have at the end: a container-backed CAS behind a local fast tier. Time: twenty minutes, most of it access configuration.
Before you start
A config file you can edit (see Configuration), and an Azure storage account you can write to.
Azure Blob is one provider value of the same cloud object store used by
S3 and compatible,
GCS and
OCI Object Storage. The composition around
it is identical; only the provider block changes.
The recipe
stores: [
{
name: "CAS_MAIN_STORE",
fast_slow: {
fast: {
filesystem: {
content_path: "/var/lib/nativelink/cas/content",
temp_path: "/var/lib/nativelink/cas/tmp",
eviction_policy: { max_bytes: "50gb" },
},
},
slow: {
experimental_cloud_object_store: {
provider: "azure",
account_name: "mystorageaccount",
container: "nativelink-cas",
key_prefix: "cas/",
retry: { max_retries: 6, delay: 0.3, jitter: 0.5 },
},
},
},
},
],account_name and container are the pair you have to set. From them the store
builds the endpoint https://{account_name}.blob.core.windows.net/{container}.
The container is a blob container inside the storage account. Create it before
starting NativeLink; the store does not create it for you.
Three ways to point at a container
Account plus container. The default above. The endpoint is derived, and
the store authenticates with Entra ID Workload Identity and nothing else: it
reads AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_FEDERATED_TOKEN_FILE
(the variables AKS workload identity injects into a pod) and fails at startup
if they are missing. A VM managed identity, a storage account key or an
az login session are not tried. Nothing secret is in the config.
An endpoint override. endpoint replaces the host portion only; the
container is still appended to whatever you give. This is how you point the
store at an emulator such as Azurite:
{
name: "CAS_SLOW_STORE",
experimental_cloud_object_store: {
provider: "azure",
account_name: "devstoreaccount1",
container: "nativelink-cas",
endpoint: "http://127.0.0.1:10000/devstoreaccount1",
},
},That address is Azurite's default. Plain http:// is accepted as-is; the
Azure store does not read insecure_allow_http. Workload Identity is still
the credential in this mode, so the three environment variables must be
present; if the emulator cannot accept such a token, use sas_url instead.
A SAS URL. sas_url takes precedence over all of the above: when it is
set, account_name, container and endpoint are ignored for URL
construction and no credential is attached, because the signature in the URL
is the credential.
{
name: "CAS_SLOW_STORE",
experimental_cloud_object_store: {
provider: "azure",
account_name: "mystorageaccount",
container: "nativelink-cas",
sas_url: "${AZURE_CAS_SAS_URL}",
},
},A SAS URL is a bearer credential with an expiry
Anyone holding the URL has whatever the signature grants, so keep it in the environment rather than the config file, and note that the store will start failing the moment the signature expires, with an authentication error. Prefer Workload Identity where you have the choice.
Options worth setting
| Field | Default | What it decides |
|---|---|---|
key_prefix | none | Namespacing inside one container; give the CAS cas/ and the AC ac/ |
endpoint | derived | Host override; ignored when sas_url is set |
sas_url | none | Full pre-signed URL; overrides the derived endpoint and the credential |
retry | Exponential backoff with jitter, shared with every other provider | |
consider_expired_after_s | 0 (never) | How old a blob may be and still count as present |
max_retry_buffer_per_request | 5 MiB | How much of a single-request upload is buffered so it can be retried |
multipart_max_concurrent_uploads | 10 | Concurrent staged-block uploads within one large blob |
Blobs under 5 MiB with a known size are uploaded in one request; anything
larger, or of unknown size, is staged as blocks of at least 5 MiB and
committed with a block list. retry and consider_expired_after_s behave as
described in the shared options section;
insecure_allow_http and disable_http2 are accepted but not read by the
Azure store.
Steps
Create the storage account and container in the region your workers run in. A standard general-purpose v2 account on hot tier is the right default; cool and archive tiers price retrieval in a way a build cache cannot afford.
Grant access. Assign the workload identity the Storage Blob Data Contributor role scoped to the container, or mint a SAS with read and write. The store never lists or deletes blobs.
Set the Workload Identity variables (
AZURE_TENANT_ID,AZURE_CLIENT_ID,AZURE_FEDERATED_TOKEN_FILE) in the process environment, unless you usesas_url. Uncommitted blocks left by a process killed mid-upload are discarded by Azure after a week on their own.Add the store as the
slowhalf of afast_slowpair whosefasthalf is a local filesystem store.Run a build twice: once cold, once after emptying the local fast tier.
You did it right if
- Blobs appear under
key_prefixin the container during the first build. - The second build is a cache hit even after
content_pathis emptied, which proves reads fall through to the container. - Container request counts are far lower than blob counts, which proves the fast tier is absorbing repeat reads.
- No uncommitted blocks accumulate between builds.
When it doesn't work
Deduplication, compression and existence caching in front of the container, the layers that decide what it actually costs.
SidewaysS3 and compatibleThe same store type with a different provider, and the shared options in full.
Google Cloud Storage
Back the cache with a GCS bucket: authentication, the resumable-upload chunk size, and the timeout fields whose names disagree with their units.
Redis
Back the cache with Redis (standalone, sentinel or cluster) and the concurrency, chunking and timeout settings that keep it from timing out under a real build.