Skip to main content
Go workspaces enable working with multiple modules simultaneously. Introduced in Go 1.18, workspaces simplify development when you need to make changes across multiple modules.

Overview

Workspaces allow you to:
  • Work on multiple modules at once
  • Make coordinated changes across modules
  • Test changes before publishing
  • Avoid manual replace directives
Workspaces are configured via a go.work file and are intended for local development. The go.work file should not be committed to version control.

Creating a Workspace

1

Initialize Workspace

Create a workspace in a parent directory:
go work init ./module1 ./module2
This creates a go.work file:
go 1.21

use (
    ./module1
    ./module2
)
2

Verify Workspace

Check which workspace file is being used:
go env GOWORK
3

Work Across Modules

Changes in one module are immediately visible to others:
cd module1
go build  # Uses local module2 if imported

The go.work File

Structure

go.work
go 1.21

use (
    ./module1
    ./module2
    ./apps/webapp
)

replace example.com/old => example.com/new v1.0.0

Directives

Specifies the Go version:
go 1.21
Lists modules in the workspace:
use ./module1
use ./module2

// Or as a block
use (
    ./module1
    ./module2
)
Replaces module paths (same as in go.mod):
replace example.com/pkg => ../other-pkg
replace example.com/old v1.0.0 => example.com/new v1.1.0
Workspace replaces take precedence over go.mod replaces.

Workspace Commands

go work init

Initialize a new workspace:
# Create empty workspace
go work init

# Create with modules
go work init ./module1 ./module2

# Create in specific directory
cd ~/projects/myworkspace
go work init ./api ./lib ./cmd

go work use

Add or update modules in workspace:
# Add single module
go work use ./module3

# Add multiple modules
go work use ./module1 ./module2

# Add all modules in directory recursively
go work use -r ./packages

go work edit

Edit go.work file programmatically:
# Add use directive
go work edit -use=./newmodule

# Drop use directive  
go work edit -dropuse=./oldmodule

go work sync

Sync workspace dependencies:
go work sync
This updates go.mod files in all workspace modules to match the workspace’s build list.
Run go work sync after adding dependencies to ensure all modules use consistent versions.

go work vendor

Create vendor directory for workspace:
# Create vendor directory
go work vendor

# Verbose output
go work vendor -v

# Custom output directory
go work vendor -o ./vendor-dir

Example Workspace Setup

Project Structure

myproject/
├── go.work
├── api/
│   ├── go.mod
│   └── main.go
├── lib/
│   ├── go.mod
│   └── lib.go
└── cmd/
    ├── go.mod
    └── main.go

Setting Up

1

Create Modules

mkdir -p myproject/{api,lib,cmd}
cd myproject

# Initialize each module
cd api && go mod init example.com/api && cd ..
cd lib && go mod init example.com/lib && cd ..
cd cmd && go mod init example.com/cmd && cd ..
2

Create Workspace

cd myproject
go work init ./api ./lib ./cmd
Creates go.work:
go 1.21

use (
    ./api
    ./cmd
    ./lib
)
3

Import Local Modules

In api/main.go:
package main

import "example.com/lib"

func main() {
    lib.DoSomething()
}
No replace directives needed!

Working with Workspaces

Building and Testing

# Build from any workspace module
cd api
go build  # Automatically uses local ./lib

# Test all workspace modules
go test ./...

# Build specific module
go build -C ./api

# Run from workspace root
go run ./api

Adding Dependencies

# Add dependency to specific module
cd api
go get github.com/gorilla/mux

# Sync workspace
go work sync

Checking Workspace Status

# Show active workspace
go env GOWORK

# List workspace modules
go list -m

# Show all modules in workspace
go work edit -print

Workspace vs Replace Directives

Before Workspaces (go.mod)

api/go.mod
module example.com/api

replace example.com/lib => ../lib

require example.com/lib v0.0.0

With Workspaces (go.work)

go.work
use (
    ./api
    ./lib
)
api/go.mod
module example.com/api

require example.com/lib v0.0.0

Replace Directives

  • Requires manual editing
  • Per-module configuration
  • Often committed by accident
  • Can be confusing

Workspaces

  • Centralized configuration
  • Automatic module discovery
  • Not committed to VCS
  • Cleaner go.mod files

Disabling Workspaces

Temporary Disable

# Disable workspace for single command
GOWORK=off go build

# Unset GOWORK
export GOWORK=off
go build

Using Different Workspace

# Use specific workspace file
GOWORK=/path/to/other.work go build

# Or set default
go env -w GOWORK=/path/to/other.work

Multi-Repository Workflows

Cloning Multiple Repositories

# Clone repositories
mkdir workspace
cd workspace
git clone https://github.com/user/module1
git clone https://github.com/user/module2
git clone https://github.com/user/app

# Create workspace
go work init ./module1 ./module2 ./app

Making Changes Across Repos

1

Make Changes

# Edit module1
cd module1
# make changes...

# Edit app to use new module1 features
cd ../app
# make changes...
2

Test Together

# Test from workspace root
go test ./...
3

Commit and Publish

# Commit module1 changes
cd module1
git add -A
git commit -m "Add new feature"
git push
git tag v1.2.0
git push --tags

# Update app to use published version
cd ../app
go get github.com/user/[email protected]
git add -A
git commit -m "Use module1 v1.2.0"
git push

Best Practices

Don't Commit go.work

Add to .gitignore - it’s for local development only

Use work sync

Keep module versions consistent across workspace

One Workspace Per Project

Avoid overlapping or nested workspaces

Clean go.mod

Remove replace directives when using workspaces

.gitignore

# Don't commit workspace file
go.work
go.work.sum

CI/CD Considerations

Workspaces are disabled in CI by default (no go.work file):
.github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.21'
      
      # Each module tested independently
      - run: cd api && go test ./...
      - run: cd lib && go test ./...
      - run: cd cmd && go test ./...

Use Cases

Developing a Library and Its Consumers

workspace/
├── go.work
├── mylib/          # Library being developed
│   └── go.mod
├── example1/       # Example application
│   └── go.mod
└── example2/       # Another application
    └── go.mod

Monorepo Development

monorepo/
├── go.work
├── services/
│   ├── api/
│   ├── worker/
│   └── scheduler/
├── pkg/
│   ├── common/
│   ├── database/
│   └── auth/
└── tools/
    └── migrator/

Testing Unreleased Changes

# Clone your fork
git clone https://github.com/you/upstream-fork lib

# Your application
cd myapp
go work init . ../lib

# Test with unreleased changes
go test ./...

Troubleshooting

# Check workspace modules
go work edit -print

# Add missing module
go work use ./missing-module

# Verify
go list -m
# Check GOWORK setting
go env GOWORK

# Should show path to go.work file
# If empty, create workspace:
go work init ./module1 ./module2
# Sync workspace build list
go work sync

# Check dependency versions
go list -m all

# View dependency graph
go mod graph

Migration from Replace Directives

1

Create Workspace

go work init ./module1 ./module2
2

Remove Replace Directives

Edit each module’s go.mod and remove local replace directives:
go.mod
 module example.com/app
 
 require example.com/lib v1.0.0
 
-replace example.com/lib => ../lib
3

Verify

go build ./...
go test ./...
4

Update .gitignore

echo "go.work" >> .gitignore
echo "go.work.sum" >> .gitignore

See Also

Modules

Go modules fundamentals

go Command

Overview of go command

Building

Building Go programs

References

Build docs developers (and LLMs) love