Installing and Running Go
Installing and Running Go
The boring way to get Go is the official toolchain from go.dev/dl. One go binary on your PATH, Go 1.27, modules on by default, and the editor you already use. This chapter stops at a module you can run and a binary you can keep.
Mental model
Go is compiled. The go command is the compiler, linker, module manager, and test runner. You install one toolchain. Each module pins a language version in go.mod. With GOTOOLCHAIN=auto (the default), go may download a matching compiler. With GOTOOLCHAIN=local, it uses only what you installed.
GOPATH still names a cache and an install directory (pkg/mod, bin). It is not where new code lives. New code lives in a directory that has a go.mod.
A terminal is the window where you type commands. PATH is the list of directories the shell searches for a program named go. A file path is a location on disk (main.go, /home/you/desk). Put the directory that contains the go binary on PATH so go version works from any folder.
Worked examples
Case 1: Prove the toolchain is on PATH
Install from go.dev/dl — the official site, not a random script. Follow that page for your OS. Then:
go versionOutput (OS and architecture match your machine):
go version go1.27.0 linux/amd64
Anything that starts with go version go1.27 is fine. If the shell says the command is not found, PATH does not include the directory that holds go.
This program prints the same facts from inside a running binary:
// where.go
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.Version())
fmt.Println(runtime.GOOS + "/" + runtime.GOARCH)
}Run:
go run where.goOutput (again, OS/arch follow the machine):
go1.27.0
linux/amd64
go run compiles a temporary executable and runs it. There is no interpreter mode.
Case 2: Ask the toolchain about itself
go env GOROOT GOPATH GOMODCACHE GOTOOLCHAIN GO111MODULETypical output:
/usr/local/go
/home/you/go
/home/you/go/pkg/mod
auto
on
| Variable | Meaning |
|---|---|
GOROOT |
Installed toolchain |
GOPATH |
Cache root: module zip cache under pkg/mod, go install binaries under bin |
GOMODCACHE |
Downloaded modules |
GOTOOLCHAIN |
auto may fetch a compiler to match go.mod; local uses only the install |
GO111MODULE |
on — modules are the default. Do not turn this off. |
You do not create $GOPATH/src/... trees for new work.
A go.mod can also name a toolchain. The go line is the language version. The optional toolchain line is which compiler auto should prefer:
module desk
go 1.27
toolchain go1.27.0
Leave GOTOOLCHAIN=auto unless you are debugging the compiler itself.
Case 3: First module
Pick an empty directory. Create the module metadata, then the program.
mkdir desk
cd desk
go mod init deskThat writes go.mod:
module desk
go 1.27
Save as main.go:
// main.go
package main
import "fmt"
func main() {
fmt.Println("desk module is running")
}Run:
go run .Output:
desk module is running
The . means “the package in this directory.” That is the habit to keep. go run main.go works for a single file; go run . is what you want once the folder is a module.
Case 4: Build a binary you can keep
Same module, same go.mod. Replace main.go with this complete program:
// main.go
package main
import "fmt"
func main() {
fmt.Println("shift board ready")
}Run:
go build -o desk .
./deskOutput:
shift board ready
go build -o desk . writes an executable named desk in the current directory. There is no VM to install on the machine that runs it (this book does not use cgo). Rebuild after you change the source; an old binary will not update itself.
Use whatever editor you already like. A language server (gopls) is optional polish. This book never assumes a particular IDE. gofmt is not optional; the editor is.
The trap
Installing Go from a distro package that lags a year, from a random curl | bash, or from a tutorial that sets GO111MODULE=off and dumps code in $GOPATH/src. The first file still runs. The project does not exist yet.
This program is fine. The layout around it is the bug: no go.mod.
// orphan.go
package main
import "fmt"
func main() {
fmt.Println("I have no module")
}Run:
go run orphan.goOutput:
I have no module
Single-file go run still compiles stdlib-only files outside a module. That convenience hides the missing go.mod. The moment you add a second package or a dependency, the folder is not a project. Run go mod init desk before the second file exists.
The same class of mistake: setting GO111MODULE=off, or pinning GOTOOLCHAIN to an older compiler “to match a blog post.” Pin go 1.27 in go.mod. Install 1.27 from go.dev.
The boring rule
- Install from go.dev/dl. Confirm with
go version. - New work starts with
go mod init. - Use
go run .andgo build -o <name> .from the module root. - Leave
GOPATHalone. Do not setGO111MODULE=off. GOTOOLCHAIN=autois fine. Thegoline ingo.modis the language pin.- The editor is yours. Format with
gofmt.
Try this
- Run
go env GOVERSION GOTOOLCHAIN. ThenGOTOOLCHAIN=local go env GOTOOLCHAINand confirm it printslocal. - In the
deskmodule, change the printed string and rungo build -o desk .again. Run./deskand confirm it is the new text, not a stale binary. - From
desk/, rungo env GOMOD. It should print the full path togo.mod. From your home directory, run it again and notice it is empty (or/dev/null).