go work: Workspace Mode for Multi-Module Repos
go.work lets you build several modules together using local source instead of published versions.
Search across all documentation pages
go.work lets you build several modules together using local source instead of published versions.
It is the supported way to develop multi-module monorepos without sprinkling temporary replace directives in every go.mod.
Workspace mode adds a go.work file listing module directories with use directives.
When GOWORK points at that file (auto-detected in parent directories), the toolchain resolves those modules to local paths.
Your individual go.mod files stay publishable; workspace config is typically local or committed only for repo-wide dev orchestration.
Quick-reference recipe card - copy-paste ready.
mkdir -p repo/{app,lib}
cd repo/lib && go mod init example.com/lib
cd ../app && go mod init example.com/app
cd ..
go work init ./app ./lib
go work use ./app ./libcd app
go run .When to reach for this:
replace => ../lib lines that break on different machine layouts.repo/
go.work
app/
go.mod
main.go
lib/
go.mod
greet.go
// lib/greet.go
package lib
func Greet() string { return "hello from lib" }// lib/go.mod
module example.com/lib
go 1.26// app/go.mod
module example.com/app
go 1.26
require example.com/lib v0.0.0// app/main.go
package main
import (
"fmt"
"example.com/lib"
)
func main() {
fmt.Println(lib.Greet())
}// go.work
go 1.26
use (
./app
./lib
)cd app
go run .What this demonstrates:
app requires example.com/lib without a pseudo-version or replace in go.mod.go.work tells the toolchain to use ./lib on disk.app alone still records a proper require for consumers.go work init creates go.work at the repo root (or chosen path).use entries list module roots relative to the work file.GOWORK=off disables workspace mode to mimic external consumers.replace in go.work (not only go.mod) can override versions workspace-wide.Workspace files are not imported by downstream modules of your libraries.
They coordinate developers and monorepo CI, not end-user graphs.
| Mechanism | Scope | Typical commit policy |
|---|---|---|
go.work + use | All listed modules in dev | Commit in monorepos for dev ergonomics |
replace in app go.mod | Building app as main | Avoid machine paths on shared branches |
replace in library go.mod | Consumers of library | Only for permanent forks |
# Add a module later
go work use ./newservice
# Sync workspace go line with modules
go work sync
# Run tests across workspace
go work edit -json
go test ./...// go.work with replace (workspace-level)
go 1.26
use (
./app
./lib
)
replace example.com/legacy => ./legacyGOWORK=off in release pipelines may hide the fact that unpublished local code is required.lib before external users can resolve app.go.work is present.Yes in team monorepos where everyone develops multiple modules together.
Solo library authors consuming their module via proxy usually omit it.
Tidy still edits each module's go.mod.
Workspace affects resolution during build, not the publishing contract inside each module.
GOWORK=off go test ./... or rename/move go.work for a single command session.
use paths are filesystem directories.
Remote modules are still fetched unless you add replace in go.work.
Aligns go toolchain lines across workspace modules and can update use directives after module changes.
No - they resolve versions from the module proxy per app go.mod.
You must tag and publish lib releases.
Each module needs its own go.mod root in use.
Workspace does not flatten nested graphs automatically.
Vendor runs per main module; workspace still resolves locals first.
Verify -mod=vendor builds in CI without go.work when testing publishability.
As many as you actively co-develop.
Split workspaces when unrelated products share a Git repo but not release cadence.
Conceptually similar for local linking, but Go keeps separate go.mod files and MVS per module.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 19, 2026