NativeLink
Configuring NativeLink

Basic configurations

Minimal NativeLink configurations for development and small teams — copy, paste, run.

Three configurations that cover ~90% of single-node use. Pick the one that matches what you need, drop it next to your cluster, run.

In-memory cache only

The quickest cache to spin up. Everything lives in RAM; restart wipes it. Good for a 10-minute demo or a CI runner with a short lifetime.

{
  stores: [
    {
      name: "CAS_MAIN_STORE",
      memory: {
        eviction_policy: { max_bytes: 1_000_000_000 }, // 1 GiB
      },
    },
    {
      name: "AC_MAIN_STORE",
      memory: {
        eviction_policy: { max_bytes: 100_000_000 }, // 100 MiB
      },
    },
  ],
  servers: [{
    listener: { http: { socket_address: "0.0.0.0:50051" } },
    services: {
      cas:          [{ instance_name: "main", cas_store: "CAS_MAIN_STORE" }],
      ac:           [{ instance_name: "main", ac_store: "AC_MAIN_STORE" }],
      bytestream:   [{ instance_name: "main", cas_store: "CAS_MAIN_STORE" }],
      capabilities: [{ instance_name: "main" }],
    },
  }],
}

Save as basic_cas.json5, then:

nativelink ./basic_cas.json5

Point any RE-API client at grpc://localhost:50051 (see Setup).

Filesystem-backed cache

Survives restarts. Suitable for a small team's shared cache, a CI worker that hosts its own cache on a persistent volume, or any single-node cluster where you don't want to lose state on reboot.

{
  stores: [
    {
      name: "CAS_MAIN_STORE",
      filesystem: {
        content_path: "/var/lib/nativelink/cas",
        temp_path:    "/var/lib/nativelink/tmp",
        eviction_policy: { max_bytes: 50_000_000_000 }, // 50 GiB
      },
    },
    {
      name: "AC_MAIN_STORE",
      filesystem: {
        content_path: "/var/lib/nativelink/ac",
        temp_path:    "/var/lib/nativelink/tmp",
        eviction_policy: { max_bytes: 500_000_000 }, // 500 MiB
      },
    },
  ],
  servers: [{
    listener: { http: { socket_address: "0.0.0.0:50051" } },
    services: {
      cas:          [{ instance_name: "main", cas_store: "CAS_MAIN_STORE" }],
      ac:           [{ instance_name: "main", ac_store: "AC_MAIN_STORE" }],
      bytestream:   [{ instance_name: "main", cas_store: "CAS_MAIN_STORE" }],
      capabilities: [{ instance_name: "main" }],
    },
  }],
}

Pick a filesystem that handles many small files

CAS blobs are typically small — function-level outputs, header files, link arguments. Default ext4 will work fine; a filesystem tuned for small files (XFS with inode64, ZFS with recordsize=4k) will degrade more gracefully under load.

Compressed filesystem cache

Same as filesystem-backed, with LZ4 compression around the CAS data. Trades a small amount of CPU for typically 40–60% storage savings on real-world build artifacts.

{
  stores: [
    {
      name: "CAS_MAIN_STORE",
      compression: {
        compression_algorithm: { lz4: {} },
        backend: {
          filesystem: {
            content_path: "/var/lib/nativelink/cas",
            temp_path:    "/var/lib/nativelink/tmp",
            eviction_policy: { max_bytes: 100_000_000_000 }, // 100 GiB
          },
        },
      },
    },
    {
      name: "AC_MAIN_STORE",
      filesystem: {
        content_path: "/var/lib/nativelink/ac",
        temp_path:    "/var/lib/nativelink/tmp",
        eviction_policy: { max_bytes: 500_000_000 },
      },
    },
  ],
  servers: [/* same as filesystem-backed */],
}

Compression happens on write; decompression on read is automatic. The client sees raw bytes either way.

Adding remote execution to any of the above

The configs above are cache-only. To accept Execute calls and run actions, add a scheduler and at least one worker:

{
  // ... stores + servers from above ...

  schedulers: {
    MAIN_SCHEDULER: {
      simple: {
        supported_platform_properties: {
          OSFamily: "exact",
          container_image: "exact",
        },
      },
    },
  },

  servers: [{
    listener: { http: { socket_address: "0.0.0.0:50051" } },
    services: {
      // ... cas / ac / bytestream / capabilities as above ...
      execution:  [{ instance_name: "main", scheduler: "MAIN_SCHEDULER" }],
      worker_api: { scheduler: "MAIN_SCHEDULER" },
    },
  }],

  workers: [{
    local: {
      worker_api_endpoint: { uri: "grpc://localhost:50051" },
      cas_fast_slow_store: "CAS_MAIN_STORE",
      upload_action_result: { upload_action_result: { ac_store: "AC_MAIN_STORE" } },
      platform_properties: {
        OSFamily: { values: ["linux"] },
        container_image: { query_cmd: "echo nativelink" },
      },
    },
  }],
}

Now the cluster will execute actions in addition to caching them.

Aligning digest functions

Every NativeLink server, worker, and build client sharing a cache must use the same digest function. SHA-256 and BLAKE3 produce different cache keys for the same bytes, so uploads made with one function are cache misses for clients using the other.

NativeLink defaults to SHA-256. Keep the default explicit in every NativeLink configuration:

{
  global: {
    max_open_files: 24576,
    default_digest_hash_function: "sha256",
  },
  // ... stores, servers, schedulers, and workers ...
}

Then configure Bazel clients to match in .bazelrc:

startup --digest_function=sha256

If your clients use BLAKE3 instead, set both values to blake3. NativeLink accepts an explicitly declared non-default function, but it logs a warning because mixed digest functions divide the cache and reduce shared cache hits.

We compared both algorithms on an Apple M2 Max with 12 CPU cores and 32 GB of memory, running macOS 15.6 and Bazel 9.1.1. Each of two alternating runs built //:nativelink against a fresh local NativeLink filesystem cache with hash and size verification enabled. Uploads were synchronous, and the Bazel output and NativeLink cache directories were isolated by algorithm.

PhaseSHA-256 runsSHA-256 meanBLAKE3 runsBLAKE3 mean
Compile and populate an empty cache179.90 s, 180.18 s180.04 s181.56 s, 185.57 s183.57 s
Rebuild from the populated cache6.39 s, 5.93 s6.16 s6.74 s, 6.41 s6.58 s

Both population builds performed 2,620 actions. Each cached rebuild reported 1,458 remote cache hits. SHA-256 was 1.9% faster while populating the cache and 6.3% faster on the cached rebuild in this test, so SHA-256 remains the default.

These are whole-build measurements from one macOS machine, not a universal hashing benchmark. Compilation, storage, scheduling, and operating-system caching dominate much of the elapsed time. Measure your own representative workload before changing algorithms; keeping every participant aligned matters more than a small isolated timing difference.

Choosing instance names

The instance_name field appears everywhere. It namespaces every artifact: action hashes from two different instance names never collide.

Pick one name per logical environment. Common patterns:

  • main — the production cache.
  • <repo-name> — a per-repo cache, for orgs that want isolation.
  • <branch-pattern>-experiments — a sandbox for non-default branches.

Every client targeting a given cache must use the same instance_name. Mismatches don't produce errors; they produce silently-empty caches.

FAQ

What's next

On this page