skip to content

How do you decide between proxy.golang.org and an internal Go module mirror for a build fleet?

level: principalimportance: should knowfreq 38%

answer

  1. three questions wearing one coat
  2. egress may not be your call
  3. public mirror durability argues for it
  4. a company-wide single point of failure
  5. decide the fallback before the outage

basics

~20 s

Decide on egress policy, availability and ownership. The public mirror is free, durable and highly available but puts every build on the public internet; an internal mirror buys allowlisting and one credential boundary, and becomes a company-wide single point of failure.

solid answer

~40 s

Frame it as three questions. First, egress: may build machines reach the public internet, and may internal module paths be resolved against public services? If not, an internal mirror is mandatory and the call is already made. Second, availability: the public mirror keeps immutable copies of versions whose upstream repositories later vanish, so an internal one must match that durability or it is a downgrade — and its outage stops every build in the company at the same minute, which makes the comma-versus-pipe fallback in `GOPROXY` a decision you write down. Third, ownership: a mirror needs an owner, an SLO and a restore plan. The usual landing place is a mirror in front of the public one, with `GOPRIVATE` covering internal prefixes. Expect security to be able to overrule you on egress.

go deeper

for a junior

Be ready to say that GOPROXY decides where modules are fetched from and that an organisation can point it at its own server instead of the public one. You are not expected to weigh the tradeoff yet.

for a middle

Explain the mechanics the decision rests on: the ordered GOPROXY list, comma versus pipe fallback, and the fact that go.sum verification happens whatever the source. Being able to describe both setups accurately is what is wanted here.

for a senior

Demonstrate that you have operated one: what breaks when the mirror is down, how you would notice, why cold-build time and credential placement change, and what you print in the build log so two machines can be compared.

for a principal

Own the whole call — egress policy, the durability an internal mirror must match, who runs it to what SLO, and what you refuse to trade away. Be explicit that the egress input belongs to security and design so their answer stays a configuration change.

## The decision, stated honestly Every build in the organisation resolves modules through whatever `GOPROXY` says. Changing that value is a two-character edit and an organisation-wide dependency change, which is what makes it a decision somebody owns rather than a preference. The question is usually posed as "should we run our own proxy?" It is better decomposed into three independent questions that happen to share an answer field. ## 1. Egress policy — often not yours to decide Can build machines reach the public internet, and may internal module paths be sent to public services as lookups? If the answer to either is no — regulated network, air-gapped build environment, a legal constraint on internal source or internal naming leaving the network — the decision is made for you and the remaining work is engineering, not judgment. Recognising this early matters: arguing the merits of a public mirror to a security team that has already ruled it out wastes the credibility you will need for the parts that *are* negotiable. Note the two distinct leaks people conflate. Fetching a *public* dependency through the public mirror discloses which open-source libraries you use. Resolving an *internal* module path against a public proxy or checksum database discloses your internal naming. The second is the one security teams actually care about, and it is fixed by scoping internal path prefixes into GOPRIVATE — not by abandoning the public mirror. ## 2. Availability — the argument that runs the other way The instinct is that an internal mirror is more reliable because you control it. For module fetching, that instinct is often wrong. The public mirror keeps **immutable copies** of every version it has served. When an upstream repository is deleted, renamed, made private or simply unreachable, builds that route through it keep working. That durability is a real property, and any internal mirror must match it — meaning its storage is now a durable artefact store with backups, not a cache you can wipe. A mirror that evicts old versions to save space has quietly made historical builds unreproducible. Against that, a mirror in the path is a new single point of failure with a blast radius of *every build in the company*. When it is down, everything stops at the same minute. That failure is survivable only if you decided in advance what it should do: - `mirror,https://proxy.golang.org,direct` — comma, so a mirror outage that returns 500 **stops** the build. Control preserved, availability sacrificed. - `mirror|https://proxy.golang.org,direct` — pipe, so any mirror error falls through to the public path. Availability preserved, and the allowlist you built silently stops applying during the outage. There is no correct answer here, only a decision that must be explicit, because the two configurations look almost identical in a config file and behave completely differently at 3am. ## 3. Ownership and cost A mirror is a service. It needs an owner, an SLO that is at least as good as the build system's, storage that grows monotonically, monitoring, and a documented restore path. "The platform team runs it alongside everything else" describes a service that will be discovered to be unmonitored during its first outage. Set against that, what a mirror actually buys: - **One credential boundary** for private modules, instead of VCS credentials distributed to every build machine. - **A place to allowlist or audit** what enters the network, which is often the security team's real ask. - **Insulation** from upstream rate limits and from a bad day at any single dependency host. If none of those three is a live problem, the honest answer is that you do not need a mirror yet, and saying so is the more valuable contribution than building one. ## What to hold firm on Whichever way the routing goes, some things are not tradeable and should be stated as such: - **`go.sum` is committed and enforced everywhere.** No mirror substitutes for it. A mirror serves bytes; it makes no claim about them. - **The checksum database stays on for public dependencies.** Turning it off globally to make private modules work is trading a real guarantee for every third-party dependency to avoid writing a pattern. Scope the exemption to internal path prefixes. - **No blanket setting that lets tooling rewrite recorded hashes to make a failure go away.** That converts a precise, loud failure into a green build with an unanswered question behind it. ## How to decide in practice It is environment configuration, which makes it unusually cheap to test and unusually easy to change by accident. Pilot the mirror on one repository's CI, measure cold-build time and failure rate, and keep `go env GOPROXY` printed in the build log so that any future divergence between two machines is one line away from being explained. Write down the fallback behaviour and the reason for the separator you chose; that sentence is what a future on-call engineer needs and what you will otherwise have to reconstruct under pressure. And expect to be overruled on the egress question specifically. That is not a failure of the analysis — it is the one input that belongs to someone else, and the rest of the design should be built so that switching GOPROXY to an internal-only value remains a configuration change rather than a project.

  • What is the strongest argument against assuming an internal mirror is more reliable?
    That it becomes a dependency of every build in the company, so its outage stops all of them at the same minute, while the public mirror it replaced was highly available and kept immutable copies of versions whose upstream repositories have since disappeared. To be a net improvement, an internal mirror must match that durability with real backed-up storage, not behave as a cache that evicts old versions.
  • Security asks you to stop all public module fetching. What do you concede and what do you push back on?
    Concede the routing: point GOPROXY at an internal mirror, which is a configuration change if the design anticipated it. Push back on anything that trades verification for convenience — keeping the checksum database on for public dependencies, keeping go.sum enforced, and scoping the private-path exemption by pattern rather than switching it off globally. Those cost nothing and are what the request was actually protecting.
  • How do you decide whether a mirror outage should fail builds or fall through to the public one?
    By asking what the mirror is for. If it exists to allowlist or audit what enters the network, falling through defeats it exactly when you are least able to notice — use a comma so an outage fails loudly. If it exists for speed and rate-limit insulation, a pipe is right. The decision must be written down, because the two configurations differ by one character and behave completely differently.

saying these in an interview costs you the question

  • Assumes an internal mirror is automatically more reliable
  • Turns the checksum database off globally to make private modules work
  • Treats the mirror as a cache that may evict old versions
  • Ignores that egress policy may belong to the security team
  • Leaves the fallback behaviour on a mirror outage undecided