Vendoring Go Modules for Reproducible Offline Builds
A task guide to vendoring Go dependencies so builds succeed without network access, covering go mod tidy, go mod vendor, offline validation, and recovery steps.
02 Jul 2026, 02:37 UTC

The problem: builds that fail without a network
When a CI runner, build server, or air-gapped environment cannot reach proxy.golang.org or your private module proxy, go build stops before compiling anything. The toolchain tries to download dependencies and fails with a network error. Vendoring removes that dependency by copying every required package into a vendor/ directory inside your module. Once that tree exists, the Go toolchain reads source code from local disk instead of the network, making the build reproducible and offline-capable.
What vendoring actually does
Vendoring is not a lockfile. It is a physical copy of the exact source code that go.mod and go.sum describe. The file vendor/modules.txt acts as a manifest: it records which module versions were copied and which packages they provide. When go.mod declares go 1.14 or higher and a consistent vendor/modules.txt is present, the toolchain automatically uses -mod=vendor without an explicit flag.
Prerequisites
- A Go toolchain version 1.14 or newer.
- An initialized module with a
go.modfile. - A clean working tree so you can review the vendor diff before committing.
- Network access for the first
go mod tidyif the module cache is empty.
Procedure: from imports to a vendor tree
1. Synchronize go.mod and go.sum
Run the following in the module root, where go.mod lives. You need write permission to the module directory.
go mod tidy
This command scans every .go file in the main module, adds missing requirements to go.mod, and records cryptographic hashes in go.sum. It also removes requirements that are no longer imported.
Risk: go mod tidy can drop dependencies that are referenced only by build constraints or generated code. If your CI uses specific build tags, run tidy with the same tags so the import graph matches production:
go mod tidy -tags=integration
2. Create the vendor directory
Still in the module root, run:
go mod vendor
This copies the packages needed by the main module into vendor/ and writes vendor/modules.txt. Do not hand-edit vendor/modules.txt; regenerate it with go mod vendor after any dependency change.
3. Build and test against the vendor tree
With the vendor directory in place, build all packages from local source:
go build -mod=vendor ./...
If go.mod specifies go 1.14 or higher, the -mod=vendor flag is implied and the command reduces to go build ./.... Run the test suite the same way:
go test ./...
Expected checks
Confirm the vendor tree is complete and consistent with these checks.
Offline build validation
The strongest check is to disable the network entirely. Set GOPROXY=off and force vendor mode:
GOFLAGS=-mod=vendor GOPROXY=off go build ./...
Expected result: the build succeeds. If it fails with a message about missing modules or inconsistent vendoring, the vendor/ tree is stale or incomplete.
Module verification
Run go mod verify from the module root. Expected output is all modules verified. This confirms that the packages in the module cache match the hashes in go.sum. Note that vendoring does not replace go.sum; keep both under version control.
Manifest consistency
Open vendor/modules.txt and compare the explicit requirements against go.mod. Every module listed as an explicit requirement should correspond to a require directive. A mismatch usually means go.mod was edited without re-running go mod vendor.
| Check | Command / action | Expected result |
|---|---|---|
| Offline build | GOFLAGS=-mod=vendor GOPROXY=off go build ./... | Build succeeds with no network |
| Tests | go test ./... | All tests pass |
| Checksums | go mod verify | all modules verified |
| Manifest | Inspect vendor/modules.txt | Entries match go.mod requirements |
Recovery options
Inconsistent vendoring
If the toolchain reports inconsistent vendoring or a missing package in vendor/, re-run go mod vendor in the module root. This regenerates the tree and modules.txt from the current go.mod.
Checksum failures
When go mod verify reports a mismatch, inspect go.sum for the affected module. Do not delete entries blindly. If the module was upgraded intentionally, re-run go mod tidy and go mod vendor to refresh hashes and the vendor tree together.
Missing package after tidy
If a build fails because a package is absent from vendor/, confirm that the package is actually imported by code in the main module. Dependencies imported only by tests outside the main module, or by tools invoked through go run, may not be vendored by default. Add the import to main-module code or re-run go mod tidy with the appropriate build tags.
Limitations and operational notes
- Repository size: A
vendor/tree contains full source code for every dependency and can be large. Commit it deliberately and consider whether your workflow benefits from committing vendor or using a proxy cache instead. - go.sum is still required: Vendoring copies files but does not remove the need for checksum verification. Keep
go.sumin version control alongsidevendor/. - Go directive matters: If
go.moddeclares a version lower than 1.14, automatic vendoring is disabled. Either raise the directive or pass-mod=vendorexplicitly on every build. - Toolchain downloads: Setting
GOPROXY=offis a strong offline check, but it also blocks automatic toolchain downloads when atoolchaindirective is present. Use it for validation, not necessarily as a permanent CI setting. - Build tags: Always run
go mod tidywith the same build tags used in CI. Otherwise, dependencies needed only under those tags may be dropped fromgo.modandvendor/.
Practical result check
After completing the procedure, run the offline build command in a shell with no network access, or simply trust GOPROXY=off as a local simulation. If go build and go test both succeed and go mod verify reports all modules verified, the module is ready for reproducible offline builds. Commit go.mod, go.sum, and the vendor/ directory together so the next clone builds without reaching the network.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.