Five Years, Three Posts, and the Unsolved Private Go Module Distribution Problem

Over five years the author refined an internal private Go module fetching system, only to discover when delivering the code to a client that the assumed network and trust boundaries broke, prompting the creation of a command‑line tool (gvu) to automate vanity import mapping and module distribution.

TonyBai
TonyBai
TonyBai
Five Years, Three Posts, and the Unsolved Private Go Module Distribution Problem

Initial private Go module fetching scheme (2021)

Uses govanityurls, Nginx, and an internal GOPROXY to translate a vanity import path such as mycompany.com/go/common into the real repository URL. Developers point their GOPROXY to the internal proxy, making the private module fetch transparent.

Evolution of the scheme

2022 – migration from Gerrit to GitLab required adjustments to vanity.yaml and SSH key algorithms, but the core translation principle remained unchanged.

2023 – a third article highlighted a manual step: each new private repository forced a change to vanity.yml. The author also investigated go mod replace as a way to bypass the vanity layer and discovered that Go already supports this feature.

All of these solutions assumed that the "code fetcher" and the "code server" lived within the same network and trust boundary.

Breaking the boundary when delivering to a client

Client machines could not reach the internal GOPROXY or VCS, so the import path mycompany.com/go/common became a dead link. The situation changed three boundaries:

Network boundary – the client cannot access the internal proxy or Git server.

Trust boundary – granting VCS credentials to the client would expose all other private repositories.

Organizational boundary – the vanity service deployed inside the company is unreachable from the client.

Re‑creating a simplified private module fetching setup inside the client’s environment required a new domain, vanity mapping, GOPROXY / GOPRIVATE configuration, and separate repository permissions, essentially repeating the original effort in an uncontrolled environment.

Generalizing the problem

Whenever code crosses a trust boundary (team‑to‑team, company‑to‑client, internal‑to‑public), the translation between the import path and the real repository becomes a fragile, manually built layer. This issue is not unique to Go; any language ecosystem that relies on private code distribution faces similar constraints. Go’s reliance on import‑path DNS resolution makes the problem especially visible.

Command‑line as the new control plane

Historically, boundary‑rebuilding tasks were handled by web UI back‑ends for configuring domains, TLS, and routing. Developers increasingly prefer concise terminal commands (e.g., gh repo create, wrangler deploy, supabase db push). Therefore, the repetitive steps of editing vanity.yml, configuring Nginx, and adjusting GOPROXY should be collapsed into a single command.

Introducing gvu

The five‑year workflow was packaged into a CLI tool named gvu (short for gomodvanityurls). It is in public beta at https://gomodvanityurls.com and automates the creation of vanity import mappings and private module distribution without manual edits to vanity.yml, Nginx, or GOPROXY.

Example client run after gvu setup:

$ go mod tidy
go: downloading mycompany.com/go/common v1.2.3
$ go build ./...

The build succeeded without errors, confirming that the boundary issue was resolved.

Conclusion

The five‑year journey shows that the private module solution works while staying inside the same internal boundary; the real pain point emerges the moment that boundary is broken. By abstracting the repetitive steps into gvu , a reusable way to cross trust boundaries safely is provided.

Original articles (for reference): https://mp.weixin.qq.com/s?__biz=MzIyNzM0MDk0Mg==∣=2247489923&idx=1&sn=024e050118b6ecfe30ce8bf78c7cc49c https://mp.weixin.qq.com/s?__biz=MzIyNzM0MDk0Mg==∣=2247492726&idx=1&sn=80720bc016f076cc8fd0d72c3f0d58bc https://mp.weixin.qq.com/s?__biz=MzIyNzM0MDk0Mg==∣=2247494329&idx=1&sn=2d543949f3f77f6fd192ecdfafc8d0c5

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Godependency managementGOPROXYgovanityurlsgvumodule proxyprivate modules
TonyBai
Written by

TonyBai

Tony Bai's tech world (tonybai.com). Not satisfied with just "knowing how", we strive for mastery. Focused on Go language internals, high-quality engineering practices, and cloud‑native architecture, exploring cutting‑edge intersections of Go and AI. Gophers who pursue technology are welcome—follow me and evolve with Go.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.