Kinoko's TIL Log

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 / VCS

Explanation

Go has three environment variables that control how modules are fetched:

VariableExample ValueControlsWhat It Means
GOPROXYhttps://internal-proxy.company.comWhere modules are downloaded fromGo only fetches from the internal proxy; if the proxy doesn’t have it, the build fails (unless you append ,direct)
GONOSUMDBcompany.com/*Checksum verification sourceMatching modules skip the public sum.golang.org for verification
GOPRIVATEcompany.com/*Shorthand for both aboveSimultaneously 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,direct

direct 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.

#go #devops #til

← Back to Main Page