Key takeaway
- Go package management in 2026: how Go modules, go.work, toolchain lines, GOPROXY and govulncheck keep enterprise Go builds reproducible and secure.
Go package management today means Go modules. A go.mod file declares the module path, the minimum Go version, an optional suggested toolchain and the exact versions of every dependency, while go.sum records cryptographic hashes so each build gets the same code. The older approaches this post once described, GOPATH-only go get, Glide and the experimental dep tool, have been replaced: dep was archived in September 2020 "in favor of Go modules", and Glide's own README now calls it "mostly unmaintained".
For enterprise teams the questions have moved on from "which tool?" to governance: where modules are downloaded from (GOPROXY), which code stays private (GOPRIVATE), how toolchain versions are pinned (the go and toolchain lines), how multi-module repositories are developed (go.work) and how known vulnerabilities are caught before release (govulncheck). This guide answers each one, with commands and code checked against the official documentation on go.dev. If you are setting up a Go service from scratch, pair it with our guide to test-driven development and unit testing in Go.
What does Go package management look like in 2026?
Since Go 1.16 the go command builds in module-aware mode by default, even when no go.mod file is present; GOPATH mode is only used if you set GO111MODULE=off. Every other piece of the dependency story (workspaces, toolchain selection, vulnerability scanning, private module authentication) is built on top of modules. The table below sets out the current status of each approach.
| Approach | What it does | When it fits | Current status (November 2026) |
GOPATH with go get |
Fetches source into one shared $GOPATH/src tree with no version pinning |
Only for maintaining very old code that has not been migrated | Legacy; module mode is the default since Go 1.16 |
| dep | Experimental official dependency tool with a manifest and lock file | Nowhere; migrate to modules | Deprecated and archived on 9 September 2020 |
| Glide | Community tool using glide.yaml and glide.lock |
Nowhere; migrate to modules | "Mostly unmaintained" per its README, which recommends Go modules |
Go modules (go.mod, go.sum) |
Versioned dependencies, checksums, minimal version selection | Every Go service and library | The standard; part of the go command |
Workspaces (go.work) |
Builds several local modules together as main modules | Developing changes that span more than one module | Available since Go 1.18 |
| govulncheck | Reports known vulnerabilities your code actually reaches | CI gates, release checks and binary audits | Maintained by the Go team; announced 6 September 2022 |
What has changed since GOPATH, dep and Glide?
The original problems were real. go get in GOPATH mode always fetched the latest commit, so two engineers could build the same service against different code, and every project on a machine shared one copy of each dependency. Glide and dep added lock files to fix this, but they sat outside the go command. Modules moved versioning into the toolchain itself, and later releases have kept tightening the model:
- Go 1.16: module mode on by default,
go install pkg@versionfor installing tools at a fixed version, and theretractdirective so authors can withdraw bad releases. - Go 1.18: workspace mode through
go.work. - Go 1.21: the
goline became a mandatory minimum, and thetoolchainline and automatic toolchain switching arrived. - Go 1.24: the
tooldirective tracks executable dependencies ingo.mod, replacing the oldtools.goblank-import workaround, andGOAUTHhandles authentication for private module fetches. - Go 1.25: the
ignoredirective excludes directories from package patterns such as./..., and thegocommand no longer adds atoolchainline when it updates thegoline. - Go 1.26:
go mod initnow writes the previous release as the defaultgoversion (Go 1.26 writesgo 1.25.0) to keep new modules compatible with supported releases, andgo fixwas rebuilt around modernisers. - Go 1.27 (released 19 August 2026):
go mod tidymerges duplicaterequireblocks into one direct and one indirect block for modules ongo 1.27or later,go testruns thestdversionvet check by default, andbzrsupport has been removed.
How do go.mod and go.sum keep builds reproducible?
A go.mod file names the module and lists its requirements. Go then uses minimal version selection (MVS) to choose the version of each dependency: the go.dev reference notes that "MVS is deterministic, and the build list doesn't change when new versions of dependencies are released." In other words, a new upstream release never changes your build until someone runs go get and commits the result.
module example.com/orders
go 1.26.0
toolchain go1.27.4
require example.com/platform v0.1.0
The go.sum file "contains cryptographic hashes of the module's direct and indirect dependencies", and every download is checked against it. By default, module downloads are also verified against the public checksum database at sum.golang.org. Commit both files. The everyday commands are:
# start a module (Go 1.26 and later write the previous release as the go version)
go mod init example.com/orders
# add or upgrade a dependency to a specific version
go get example.com/platform@v0.1.0
# add missing requirements, remove unused ones, fix go.sum
go mod tidy
# confirm nothing in the module cache changed since download
go mod verify
# track a developer tool in go.mod (Go 1.24 and later), then run it
go get -tool golang.org/x/vuln/cmd/govulncheck@latest
go tool govulncheck ./...
Build commands do not edit go.mod silently. If a vendor directory is present and the go line is 1.14 or higher, the go command behaves as if -mod=vendor were set and uses no network or module cache; otherwise it behaves as -mod=readonly and reports an error when go.mod needs changing. go mod vendor creates that directory, which some regulated teams keep for fully offline builds.
When should you use a go.work workspace?
Use a workspace when a change spans several modules, for example a shared platform library and the microservice that consumes it. In workspace mode the go.work file sets the main modules used as roots for resolution, so local edits to the library are picked up immediately, without temporary replace directives in go.mod. Workspaces also keep a go.work.sum file for hashes not covered by the modules' own go.sum files.
The example below has two modules. The shared package lives in platform/health/health.go:
// Package health reports service status in a shared format.
package health
import "fmt"
// Status describes the state of one service.
type Status struct {
Service string
Ready bool
}
// String returns a short, log-friendly summary.
func (s Status) String() string {
state := "not ready"
if s.Ready {
state = "ready"
}
return fmt.Sprintf("%s: %s", s.Service, state)
}
The service in orders/main.go imports it:
package main
import (
"fmt"
"example.com/platform/health"
)
func main() {
s := health.Status{Service: "orders", Ready: true}
fmt.Println(s)
}
From the repository root, create the workspace and run the service:
go work init ./orders ./platform
go run ./orders
# prints: orders: ready
# check the service still builds as consumers will build it
cd orders && GOWORK=off go build .
The last command matters. With GOWORK=off the build fails until example.com/platform v0.1.0 is actually published and recorded in go.sum, which is exactly the check you want before release. Our recommendation is to treat go.work as a developer convenience: keep it out of version control for independently released modules, and only commit it in a monorepo where every path in it is valid for every engineer and CI also runs a GOWORK=off build.
How do go and toolchain lines control the Go version?
The go line "declares the minimum required Go version for using the module or workspace", and a toolchain refuses to load a module that needs a newer version than itself. The toolchain line declares a suggested toolchain, which the go command runs when the local default is older. With the default GOTOOLCHAIN=auto, the go command downloads the required toolchain as a module (golang.org/toolchain), so the download goes through your GOPROXY and is checked against the checksum database.
| Setting | Effect | When it fits |
GOTOOLCHAIN=auto (default) |
Uses the local toolchain, switching to and downloading a newer one if go or toolchain lines require it |
Developer machines and CI with proxy access |
GOTOOLCHAIN=path |
Like auto, but only searches PATH; never downloads | Air-gapped or tightly controlled build hosts |
GOTOOLCHAIN=local |
Always runs the bundled toolchain | Builds that must fail rather than switch versions |
GOTOOLCHAIN=go1.27.4 |
Always runs that exact toolchain | Reproducing a specific release build |
Change versions with go get go@1.27.0 or go get toolchain@go1.27.4 rather than editing the file by hand, and remove a suggested toolchain with go get toolchain@none. Go's release policy is that "each major Go release is supported until there are two newer major releases". As of 12 November 2026 that means Go 1.27 (latest patch 1.27.4) and Go 1.26 (1.26.11), both patched on 10 November 2026; Go 1.25 is no longer supported.
How should enterprises configure GOPROXY, GOPRIVATE and GOSUMDB?
These environment variables decide where code comes from and how it is verified, so they belong in platform engineering policy rather than individual laptops.
| Variable | Default | What it controls | Typical enterprise setting |
GOPROXY |
https://proxy.golang.org,direct |
Comma-separated module proxies, or direct (version control) or off |
An internal caching proxy first, so builds survive upstream deletions and are logged |
GOPRIVATE |
Not set | Glob patterns of private module paths; default for GONOPROXY and GONOSUMDB | Your company's code hosts, for example *.corp.example.com |
GOSUMDB |
sum.golang.org |
Checksum database used to verify public modules | Keep the default; off removes a key supply-chain check and also blocks toolchain downloads |
GOAUTH |
netrc | Credentials for private HTTPS fetches (.netrc, git credential fill or a custom command) |
A command that issues short-lived tokens from your identity provider |
GOINSECURE |
Not set | Module paths that may be fetched over HTTP | Leave empty |
go env -w GOPRIVATE=*.corp.example.com
go env -w GOPROXY=https://goproxy.corp.example.com,https://proxy.golang.org,direct
Note that GOPRIVATE and GONOSUMDB do not apply to toolchain downloads. For a wider view of controls in code, see our guides to secure coding practices and compliance as code with OPA and CI/CD.
How does govulncheck fit into the delivery pipeline?
govulncheck reads the Go vulnerability database (vuln.go.dev), where every report is reviewed by the Go Security team, and uses call-graph analysis to surface only "vulnerabilities that actually affect you, based on which functions in your code are transitively calling vulnerable functions". That makes it far quieter than scanners that flag every module in go.sum. By default it sends only module paths to the database.
# install, or use the tool directive shown earlier
go install golang.org/x/vuln/cmd/govulncheck@latest
# scan source from the module root
govulncheck ./...
# scan a built binary (no call stacks in the output)
govulncheck -mode binary ./bin/orders
# machine-readable output for security tooling
govulncheck -format sarif ./...
In text mode govulncheck exits non-zero when it finds vulnerabilities, so it can gate a pipeline directly; with -json, -format sarif or -format openvex it exits 0, so the gate must read the report. Know the limits: calls through reflection are not visible in source scans, interface and function-pointer calls are analysed conservatively, and binary scans may report unreachable code.
In our own engineering teams, dependency vulnerability findings from scheduled repository scans are handled as priority fixes: upgrade the affected library, then validate the service end to end before release, with lock files reviewed as part of security remediation. Policy-driven controls such as AgentGRC help governance, risk and controls teams keep that evidence traceable, and ElixirClaw, an agentic OS platform to build, orchestrate and govern AI agents and automation workflows while keeping humans in control, can run the scan-triage-upgrade loop with an engineer approving each change.
What is a sensible adoption sequence for enterprise Go teams?
- Migrate legacy projects. Run
go mod initin each project still on GOPATH, Glide or dep, thengo mod tidy, and delete the old manifest and lock files once the build and tests pass. - Set the toolchain policy. Raise
golines to a supported release, decide whether CI usesGOTOOLCHAIN=autoorpath, and stop supporting Go 1.25. - Centralise module sources. Stand up an internal proxy, set
GOPRIVATEandGOAUTHthrough managed configuration, and keepGOSUMDBon. - Add checks to CI. Fail the build if
go mod tidychangesgo.modorgo.sum, rungo mod verify, run the test suite (see our Go testing guide) and gate ongovulncheck ./.... - Track tools as dependencies. Move linters, generators and govulncheck into
tooldirectives so every engineer runs the same versions. - Introduce workspaces deliberately. Use
go.workfor cross-module changes, and keep aGOWORK=offbuild in CI.
For language-level conventions that sit alongside this, see Golang best practices and benefits.
What are the trade-offs, and when should you not use each option?
| Option | Benefit | Cost or risk | Avoid when |
| Vendoring | Offline, self-contained builds | Larger repositories and noisy diffs on upgrades | A reliable internal proxy already caches every module |
Committed go.work |
One shared layout for a monorepo | Can hide missing releases and break builds for other paths | Modules are released independently to outside consumers |
GOTOOLCHAIN=auto |
Engineers always get the version a module needs | Unplanned downloads on build hosts | Air-gapped or change-controlled environments (use path) |
replace directives |
Quick local fixes or forks | Easy to ship by accident and hard to spot in review | A workspace or a tagged fork release would do |
GOSUMDB=off or GONOSUMDB on public paths |
Fewer network calls | Loses tamper detection on public modules and blocks toolchain downloads | Almost always; scope it to private paths through GOPRIVATE |
To map one dependency or release decision your engineering agents should make, and the context and policy it depends on, book a Context Workshop. See what happens in the workshop.
Frequently asked questions about Go package management
What is the standard package manager for Go?
Go modules, built into the go command. Module-aware mode has been the default since Go 1.16, and the older dep tool was archived in September 2020 in favour of modules.
Should I commit go.sum and go.work?
Always commit go.sum, because it holds the hashes that verify every dependency. Treat go.work as a local development file unless you run a monorepo where its paths are valid for everyone, and keep a GOWORK=off build in CI.
What is the difference between the go line and the toolchain line?
The go line is the minimum Go version needed to use the module. The toolchain line is a suggested toolchain that the go command switches to when the local one is older, downloading it through GOPROXY when GOTOOLCHAIN=auto.
How do I keep private modules out of the public proxy and checksum database?
Set GOPRIVATE to glob patterns for your private module paths. It is the default for GONOPROXY and GONOSUMDB, so matching modules are fetched directly and not checked against sum.golang.org. Use GOAUTH for credentials.
Does govulncheck report every vulnerable dependency?
No. In source mode it reports only vulnerabilities in functions your code actually reaches, which reduces noise. Calls through reflection are not visible, and binary mode cannot show call stacks, so pair it with code review and regular upgrades.
Which Go versions are supported in November 2026?
Go 1.27 and Go 1.26. Each major release is supported until there are two newer major releases, so Go 1.25 left support when Go 1.27 was released on 19 August 2026.
Explore the XenonStack platform family: ElixirData · ElixirClaw · ElixirHub · NexaStack · MetaSecure · Neural AI · AgentGRC · AgentSRE · AgentAnalyst · AgentInstruct · AgentRAI · AgentScrum · AgentSearch · AgentForce · AI Aviator · SAIF Aviator · CohortIQ · XenonStack AI · XenonStack UAE