Configure Go Remote Build Cache

Last updated: August 14, 2026

What is Go Build Cache

The Go toolchain has a built-in Build Cache that reuses outputs from previous builds when the inputs to a build action have not changed.

By default, Go stores this cache locally in the directory configured by GOCACHE. Go can also delegate build-cache storage to an external program through GOCACHEPROG.

There are multiple Open Source implementations of GOCACHEPROG.

platacard/cacheprog is a feature-rich GOCACHEPROG implementation that adds Remote Cache Storage to the Go Build Cache, with support for artifact compression.

BuildFetch Cache natively implements the platacard/cacheprog HTTP Remote Cache Storage protocol in Cloud & On-Prem, allowing Go builds to use BuildFetch as shared Remote Cache.

A Go build is made up of many individual actions: compiling packages, linking binaries, etc.

For each action, Go toolchain computes an ActionID from the complete description of that computation. This includes information such as the command being executed, relevant environment, input files, toolchain executables, build options, and other inputs that can affect the result.

The result of the action is identified separately by an OutputID.

  • On a Cache Miss, Go executes the action and sends its resulting output to GOCACHEPROG for storage.
  • On a Cache Hit, GOCACHEPROG restores the previously generated output and Go skips that expensive action.

Because caching happens at the build-action level rather than for one entire repository build, a build can reuse some packages while rebuilding only the parts whose inputs changed.

Schematic example: say a first run of go build ./... needs to compile a package whose action is not yet present in the Remote Cache:

Diagram
go build ./...

  Build action inputs
  +----------------------+
  | Go source files      |
  | dependency inputs    |
  | compiler/toolchain   |
  | build flags          |
  | relevant environment |
  | ...                  |
  +----------+-----------+
             |
             v
  Go computes ActionID
  from the action inputs
             |
             v
  +----------------------+
  | GOCACHEPROG          |
  | (platacard/cacheprog)|
  +----------+-----------+
             |
             v
  GET remote cache entry
  for ActionID a3f9...c2
             |
             v
  +----------------------+
  | BuildFetch Cache     |
  | key: a3f9...c2       |
  | status: MISS         |
  +----------+-----------+
             |
             v
  Compile / link action
             |
             v
  Produce output + OutputID
             |
             v
  GOCACHEPROG uploads output
  to BuildFetch Cache

Later, the same build action on another machine can produce the same ActionID. platacard/cacheprog finds the corresponding object in Remote Cache, restores it locally, and Go can skip executing that action:

Diagram
go build ./...  (same action inputs on another machine)

  Inputs unchanged
  source · dependencies · toolchain · flags · environment
             |
             v
  Same ActionID: a3f9...c2
             |
             v
  +----------------------+
  | GOCACHEPROG          |
  +----------+-----------+
             |
             v
  GET remote cache entry
  for ActionID a3f9...c2
             |
             v
  +----------------------+
  | BuildFetch Cache     |
  | key: a3f9...c2       |
  | status: HIT          |
  +----------+-----------+
             |
             v
  Download cached output
             |
             v
  Restore output locally
             |
             v
  Skip expensive action
  Build graph continues sooner

The Go Build Cache can also reuse successful package test results in supported go test scenarios.

Go provides debugging options such as GODEBUG=gocachehash=1 and GODEBUG=gocachetest=1 for investigating cache behavior.

How platacard/cacheprog connects Go to BuildFetch Cache

There are two protocols involved:

Diagram
+----------------------+
| Go command           |
| go build / go test   |
+----------+-----------+
           |
           | GOCACHEPROG
           | JSON get / put protocol
           v
+----------------------+
| platacard/cacheprog  |
+----------+-----------+
           |
           | HTTP remote storage protocol
           | GET / PUT cache objects
           v
+----------------------+
| BuildFetch Cache     |
+----------------------+

Go starts the command configured in GOCACHEPROG as a child process and communicates with it using JSON messages over stdin and stdout.

A get operation looks up an ActionID. A put operation supplies the ActionID, OutputID, and the resulting cache data.

platacard/cacheprog translates those operations to its configured remote storage backend.

With its HTTP backend, individual cache entries are addressed with requests such as:

GET /cache/<action-id>
PUT /cache/<action-id>

The HTTP protocol also transports metadata associated with the cache entry, including its OutputID.

platacard/cacheprog uses local disk as the staging area required by the Go GOCACHEPROG protocol and can compress remotely stored cache objects.

BuildFetch implements this HTTP storage protocol directly, so platacard/cacheprog can point its HTTP storage URL at a BuildFetch Cache Project.

Share Go Build Cache Across Machines

Go's default GOCACHE is local to one machine.

To reuse those build outputs on different CI agents, engineer workstations, and AI agent environments, configure GOCACHEPROG with a remote-capable implementation such as platacard/cacheprog.

BuildFetch Cache natively implements the platacard/cacheprog HTTP backend and acts as the shared remote storage for those Go cache objects.

A Remote Go Build Cache lets teams share build outputs across engineer workstations, CI, and AI agent environments.

CI, having a more reproducible and controlled environment, typically both reads from and writes to the shared Go Build Cache. Engineers and AI agents can use read-only access to reuse those outputs without publishing new cache entries.

That keeps the cache warm from reproducible CI builds while reducing repeated compilation and other build work across machines.

Unlike copying or archiving an entire GOCACHE directory between CI runs, platacard/cacheprog lets Go retrieve the individual objects it actually requests.

Go only reuses a cached result when the corresponding action identity matches.

For high cache hit rates across machines, keep the Go toolchain, build flags, source and dependency inputs, target platform, and relevant build environment consistent.

cgo note: the Go Build Cache does not automatically detect every change to external C libraries imported through cgo. If external C libraries change, ensure affected Go outputs are rebuilt rather than relying on stale cached results.

The Go Build Cache is also separate from the downloaded Go module source cache.

GOCACHEPROG is concerned with build and test cache objects. Module download caching should be managed separately when needed.

Alternative: Bazel Remote Cache with rules_go

Another way to get Remote Build Cache for Go is to build Go projects with Bazel using rules_go.

rules_go provides Bazel rules for building Go libraries, binaries, and tests, including go_library, go_binary, and go_test.

In this setup, Bazel manages the build graph and caching rather than the Go toolchain's GOCACHE / GOCACHEPROG mechanism.

Both approaches allow Go build work to be shared across machines:

  • Go Build Cache + platacard/cacheprog keeps builds using the standard go build / go test workflow and extends the Go toolchain's native Build Cache with remote storage.
  • Bazel + rules_go builds the Go project through Bazel and uses Bazel Remote Cache for the resulting build actions.

Choose the approach that matches how the project is built.

Projects using the standard Go toolchain can use platacard/cacheprog, while projects already built with Bazel can use BuildFetch's native Bazel Remote Cache support directly.

Cached Go Build Example

After CI populates the Go Remote Build Cache, a build on a fresh machine, AI agent, or ephemeral CI agent can retrieve many unchanged build actions from BuildFetch.

For example, configure platacard/cacheprog to use the BuildFetch HTTP endpoint:

export BUILDFETCH_CACHEPROG_TOKEN="<generated-token>"

export CACHEPROG_REMOTE_STORAGE_TYPE="http"
export CACHEPROG_HTTP_STORAGE_BASE_URL="https://cache.region.buildfetch.com/project-id/gocacheprog"
export CACHEPROG_HTTP_STORAGE_EXTRA_HEADERS="Authorization:Bearer ${BUILDFETCH_CACHEPROG_TOKEN}"

export GOCACHEPROG="/usr/local/bin/cacheprog"

First build, where CI encounters cache misses and populates BuildFetch Cache:

Terminal
$ time go build ./...

... compile uncached packages ...
... upload generated cache objects through cacheprog ...

real    10m4s

A subsequent build with matching inputs on another machine can retrieve those individual outputs from BuildFetch rather than repeating the expensive work:

Terminal
$ time go build ./...

... cacheprog retrieves matching build objects from BuildFetch ...

real    4s

The timings above are illustrative.

Actual performance depends on the project, amount of parallel work, cache hit ratio, network latency, and size of the cache objects.

Normal go build output is often quiet on success, so individual cache hits may not be obvious from standard build output.

For troubleshooting, platacard/cacheprog logging and Go's GODEBUG cache diagnostics can be used to inspect remote operations and cache hash behavior.

When Go Remote Build Cache helps most

Remote Go Build Cache is especially valuable in ephemeral, autoscalable CI environments, where CI agents are regularly recreated without durable local GOCACHE state.

It also helps large Go repositories and Monorepos where many packages are compiled repeatedly across branches, pull requests, CI jobs, engineer workstations, and AI coding-agent environments.

CI can continuously warm the shared BuildFetch Cache as the codebase evolves.

Other machines then reuse unchanged package and build-action outputs rather than recompiling everything from a cold local cache.

The benefit is highest when builds contain substantial reusable work and environments are sufficiently consistent to generate matching cache ActionIDs.

Configure Go Build Cache

Before setting up Remote Go Build Cache, ensure you have created a BuildFetch Project and Token(s).

platacard/cacheprog: https://github.com/platacard/cacheprog

Official Go Build Cache documentation: https://go.dev/cmd/go/#hdr-Build_and_test_caching.

BuildFetch Project Cache Setup will provide the HTTP cache URL and generated Token required by cacheprog.

platacard/cacheprog uses the following configuration for its HTTP backend:

  • CACHEPROG_REMOTE_STORAGE_TYPE=http
  • CACHEPROG_HTTP_STORAGE_BASE_URL
  • CACHEPROG_HTTP_STORAGE_EXTRA_HEADERS
  • GOCACHEPROG, which tells Go how to launch the platacard/cacheprog executable

The HTTP backend supports additional request headers, allowing the BuildFetch Token to be supplied through an Authorization header.

Generate Tokens

We recommend generating a cache:readwrite Token for CI and cache:readonly Token(s) for engineer and AI agent machines.

Generally, there are two ways to manage engineer Tokens:

  • For simpler setups, issuing and rotating a single cache:readonly Token could be sufficient.
  • For precise access control, each engineer should register in BuildFetch, then issue and rotate their own individual cache:readonly Token(s).

Generating cache:readwrite Tokens requires Project/Org Admin scope.

For Open Source Projects, we recommend not storing a cache:readonly Token in publicly accessible code because it could lead to misuse and unintended exhaustion of monthly Project limits.

Keep generated Tokens in your CI secret store or local credential environment; do not commit them to source control.

Install platacard/cacheprog

Install a recent platacard/cacheprog release on each machine that will use the Remote Cache.

For CI, using a prebuilt release or including the binary in your CI image avoids rebuilding the cache helper itself on every job.

The binary only needs to be available to the command configured in GOCACHEPROG:

export GOCACHEPROG="/usr/local/bin/cacheprog"

platacard/cacheprog supports multiple remote storage backends.

For BuildFetch Cache, use the native HTTP backend.

Configure CI with read-write cache access

On CI, configure the BuildFetch endpoint and a cache:readwrite Token:

export BUILDFETCH_CACHEPROG_TOKEN="<generated-readwrite-token>"

export CACHEPROG_REMOTE_STORAGE_TYPE="http"
export CACHEPROG_HTTP_STORAGE_BASE_URL="https://cache.region.buildfetch.com/project-id/gocacheprog"
export CACHEPROG_HTTP_STORAGE_EXTRA_HEADERS="Authorization:Bearer ${BUILDFETCH_CACHEPROG_TOKEN}"

export GOCACHEPROG="/usr/local/bin/cacheprog"

go build ./...

cacheprog receives Go's cache lookups and writes through GOCACHEPROG, retrieves existing objects from BuildFetch on hits, and uploads newly produced objects on misses.

You can use the same configuration with cacheable Go test operations:

go test ./...

Configure engineers and AI agents with read-only access

For engineer workstations and AI agents, use a cache:readonly BuildFetch Token.

platacard/cacheprog supports disabling remote cache writes, allowing these environments to consume CI-produced cache entries without publishing new ones.

export BUILDFETCH_CACHEPROG_TOKEN="<generated-readonly-token>"

export CACHEPROG_REMOTE_STORAGE_TYPE="http"
export CACHEPROG_HTTP_STORAGE_BASE_URL="https://cache.region.buildfetch.com/project-id/gocacheprog"
export CACHEPROG_HTTP_STORAGE_EXTRA_HEADERS="Authorization:Bearer ${BUILDFETCH_CACHEPROG_TOKEN}"

export GOCACHEPROG="/usr/local/bin/cacheprog --disable-put"

go build ./...

This gives the typical BuildFetch cache topology:

Diagram
                     +----------------------+
                     | BuildFetch Cache     |
                     | shared Go objects    |
                     +----------+-----------+
                                ^
                                |
                  read + write  |  read only
                     +----------+----------+
                     |                     |
             +-------+-------+     +-------+-------+
             | CI agents     |     | Engineers     |
             | cacheprog     |     | cacheprog     |
             | read-write    |     | --disable-put |
             +---------------+     +---------------+
                                           ^
                                           |
                                    +------+------+
                                    | AI agents   |
                                    | read-only   |
                                    +-------------+

CI populates BuildFetch from reproducible builds while engineers and AI agents reuse those cached results across machines.

Troubleshooting Go cache hits

Go includes several build-cache debugging options:

# Print the inputs that contribute to Go cache hashes.
GODEBUG=gocachehash=1 go build ./...

# Debug Go test cache decisions.
GODEBUG=gocachetest=1 go test ./...

Go also provides:

GODEBUG=gocacheverify=1 go build ./...

gocacheverify bypasses normal cache reuse while checking cached results against freshly computed outputs.

These debugging options can produce substantial output and are best used for cache investigation rather than normal builds.