GOPROXY
The Point
Fetching private Go modules directly from GitHub in CI/CD easily gets rate-limited (403) when there are too many requests. The fix is to set GOPROXY in your CD yaml to point at an internal Athens proxy server – Athens caches the modules, so subsequent builds never hit GitHub again.
CI / go build
|
Athens (internal proxy, with cache)
| (only on first fetch)
GitHub / VCSExplanation
Go has three environment variables that control how modules are fetched:
| Variable | Example Value | Controls | What It Means |
|---|---|---|---|
GOPROXY | https://internal-proxy.company.com | Where modules are downloaded from | Go only fetches from the internal proxy; if the proxy doesn’t have it, the build fails (unless you append ,direct) |
GONOSUMDB | company.com/* | Checksum verification source | Matching modules skip the public sum.golang.org for verification |
GOPRIVATE | company.com/* | Shorthand for both above | Simultaneously skips the public proxy and public checksum DB |
GOPROXY fallback chain
GOPROXY supports comma-separated sources – Go tries them in order:
GOPROXY=https://internal-proxy.company.com,directdirect is not another proxy – it means fetching directly from VCS (GitHub). If the internal proxy doesn’t have a particular public module, it falls back to direct.
Knowledge Sugar
Why can’t private modules be sent to sum.golang.org?
sum.golang.org is a public service. By default, Go sends the module path there for checksum verification. If a private module’s path gets sent, it leaks internal repo information. Use GONOSUMDB or GOPRIVATE to exclude them.
GOPRIVATE vs setting GOPROXY + GONOSUMDB separately
GOPRIVATE=company.com/* is equivalent to setting both GONOSUMDB=company.com/* and GONOPROXY=company.com/* (skip proxy, go straight to VCS). If you’re using an internal Athens proxy, you usually still need to explicitly set GOPROXY, because GOPRIVATE tells Go “don’t use a proxy” – which would bypass your Athens instance and hit GitHub directly.
How to pass a GitHub token in Docker builds?
If your Dockerfile runs go build and needs to fetch private modules, don’t use ARG for the token – ARG values end up in the build history and can be recovered from image layers even after the file is deleted.
The correct approach is Docker BuildKit’s secret mount:
1RUN --mount=type=secret,id=github_token \
2 GITHUB_TOKEN=$(cat /run/secrets/github_token) \
3 go build ./...The secret is only visible during this RUN step and is never written to any image layer.