Skip to content

Go Modules Reference: go.mod, go.sum, Commands, Workspaces, GOPROXY & Private Repos

Go Modules (introduced in Go 1.11, stable in 1.13) is Go’s dependency management system. go.mod declares your module path and dependencies; go.sum is the cryptographic checksum file. Understanding module commands, versioning, and common gotchas will save you hours of debugging dependency issues.

1. go.mod — File Structure & Module Path

What every field in go.mod means
# go.mod example:
module github.com/my-org/my-service    # module path — must match repo URL for public modules

go 1.22                                 # minimum Go version required (not the version you compiled with)

require (
    github.com/gin-gonic/gin v1.9.1     # direct dependency
    github.com/stretchr/testify v1.8.4  // indirect comment below
    golang.org/x/net v0.20.0            // indirect
)

# // indirect = not directly imported in your code (transitive dep kept in go.mod for reproducibility)
# After 'go mod tidy', all indirect deps are listed here

replace (
    github.com/some/module v1.2.3 => ./local-fork    # replace with local version
    github.com/some/module v1.2.3 => github.com/my-fork/module v1.2.4  # replace with fork
)

retract v1.0.0   # mark version as unsafe (appears in go list, warns users to upgrade)

# go.sum — DO commit this file; DO NOT edit manually:
# github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
# github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
# (two lines per dep: source hash + go.mod hash)

2. Essential Commands

go mod init, tidy, download, vendor, verify, why
# Initialize a new module:
go mod init github.com/my-org/my-service

# Add a dependency (automatically updates go.mod + go.sum):
go get github.com/gin-gonic/gin@v1.9.1      # specific version
go get github.com/gin-gonic/gin@latest       # latest release
go get github.com/gin-gonic/gin@main         # latest commit on main branch
go get github.com/gin-gonic/gin@v2           # major version 2

# Remove unused deps + add missing transitive deps:
go mod tidy
# Run this: after deleting imports, after adding new imports, before committing

# Download all deps to local cache ($GOPATH/pkg/mod):
go mod download

# Vendor mode (copy deps into ./vendor/ — for reproducible offline builds):
go mod vendor
go build -mod=vendor ./...    # use vendor/ instead of cache

# Verify deps haven't been tampered with (compares against go.sum):
go mod verify

# Why is this package a dependency?
go mod why github.com/some/package

# List all deps (including transitive):
go list -m all

# Check for available updates:
go list -m -u all | grep "\["     # shows available upgrades in brackets
# Output: github.com/gin-gonic/gin v1.9.1 [v1.9.1 → v2.0.0]

# Upgrade all deps to latest minor/patch:
go get -u ./...
go mod tidy

3. Versioning & Semver

Semantic import versioning, pseudo-versions, and pre-releases
# Go module versioning rules:
# v0.x.y — no stability guarantees (breaking changes OK)
# v1.x.y — stable, no breaking changes in minor/patch
# v2+ — MUST have /v2 in module path (semantic import versioning)

# Using a v2+ module:
import "github.com/some/module/v2"           # /v2 is REQUIRED in import path
# In go.mod:
require github.com/some/module/v2 v2.1.0

# Pseudo-versions (when using a commit, not a tag):
go get github.com/some/module@abc1234         # specific commit
# go.mod shows: github.com/some/module v0.0.0-20240301123456-abc1234abcde
# Format: vX.Y.Z-yyyymmddhhmmss-12hexdigits

# Pre-release versions:
go get github.com/some/module@v1.3.0-beta.1
go get github.com/some/module@v1.3.0-rc.2

# Minimal version selection (MVS) — Go's algorithm:
# When multiple deps require different versions of the same package,
# Go picks the MINIMUM version that satisfies ALL constraints.
# No "latest" auto-upgrade — you always know exactly what version you get.
# This is why go.sum is stable and builds are reproducible.

# Downgrade a dep:
go get github.com/gin-gonic/gin@v1.8.0       # pin to older version

# Exclude a broken version:
# In go.mod:
exclude github.com/some/module v1.2.0        # skip this specific version

4. Workspaces (Go 1.18+)

go.work for multi-module local development without replace directives
# Problem: developing two modules simultaneously (my-app + my-lib)
# Old way: use 'replace' in go.mod to point at local fork — messy, easy to commit by mistake
# Better: go workspaces (go 1.18+)

# Project structure:
# ~/projects/
#   my-app/   go.mod (requires my-lib v1.2.0)
#   my-lib/   go.mod

# Initialize workspace:
cd ~/projects
go work init ./my-app ./my-lib
# Creates go.work file:
# go 1.22
# use ./my-app
# use ./my-lib

# my-app now automatically uses local my-lib (no replace needed)
go work sync    # sync workspace with go.mod dependencies

# Add another module to workspace:
go work use ./another-module

# go.work vs go.work.sum:
# go.work — commit if you want team to share workspace config
# .gitignore go.work for personal dev setups that shouldn't affect team

# Run commands in workspace context:
cd my-app && go build ./...     # uses local my-lib automatically
go test ./...                   # tests across all workspace modules

# Disable workspace temporarily:
GOWORK=off go build ./...       # ignores go.work, uses go.mod as-is

5. GOPROXY, GOPRIVATE & Module Mirror

Control where Go downloads modules — proxy, private repos, GONOSUMCHECK
# Default GOPROXY chain:
# GOPROXY=https://proxy.golang.org,direct
# 1. Try proxy.golang.org (Google's module mirror) — fast + cached
# 2. If not found: try direct (original VCS)

# Check/set proxy:
go env GOPROXY
go env -w GOPROXY=https://proxy.golang.org,direct

# Private repos (don't go through public proxy):
go env -w GOPRIVATE=github.com/my-private-org,gitlab.com/internal-group
# GOPRIVATE also sets GONOSUMCHECK (skip checksum verification for private modules)
# For just skipping checksum: go env -w GONOSUMCHECK=github.com/my-private-org

# Corporate proxy (air-gapped environments):
go env -w GOPROXY=https://my-corp-proxy.internal,off
# 'off' means fail if proxy doesn't have it (don't try direct)

# Athens — self-hosted module proxy (for air-gapped or compliance):
# docker run -p 3000:3000 gomithrandir/athens:latest
go env -w GOPROXY=http://localhost:3000,direct

# Common CI setup (fast + offline-safe):
# 1. Download all deps first:
go mod download
# 2. Build using cache (no network needed):
GOFLAGS="-mod=readonly" go build ./...
# -mod=readonly: fail if go.mod needs updating (catches "forgot to run go mod tidy")

# Debugging module issues:
GOFLAGS=-v go get github.com/some/module   # verbose download
go env GOPATH                               # where modules cache lives
ls $(go env GOPATH)/pkg/mod/cache/download # inspect the module cache

Track Go releases and module tooling updates.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: Go Reference | GitHub Actions Reference | Trivy Reference | Go EOL Tracker

🔍 Free tool: Go Module Health Checker — check any Go module for latest version, known CVEs, and active maintenance — before go get.

Founded

2023 in London, UK

Contact

hello@releaserun.com