Installing Go
Before you can write a single line of Go, you need a working Go toolchain on your machine: the compiler, the standard library, and command-line tools like go build, go run, and go test. Installing Go itself is a five-minute task on every major operating system, but understanding what the installer actually sets up — where the toolchain lives, how the go command finds your source code, and what GOPATH means today versus what it meant a few years ago — will save you from confusing errors down the road. This lesson walks through installing Go on Windows, macOS, and Linux, verifying the install, and writing and running your first program.
Overview: What Installing Go Actually Sets Up
“Installing Go” means putting the official Go SDK on disk: the go command-line tool, the gc compiler and linker, gofmt for formatting, and the full standard library source (used both to compile your programs and so your editor can jump to definitions inside packages like fmt or net/http). All of this comes from a single download at go.dev, or from a trusted package manager that repackages the same release.
Two environment variables matter here, and it helps to know what each one is for. GOROOT is the directory where the Go installation itself lives — the compiler binaries and standard library. The installer sets this for you automatically, and you should almost never need to change it by hand. GOPATH is different, and its meaning has changed over Go’s history. Before modules existed (pre-2019), every project you wrote had to live inside a rigid $GOPATH/src/import/path directory tree, and all your dependencies were checked out as mutable source trees inside that same tree. Since Go 1.16, module mode is the default everywhere: your project can live in any directory you like, and it declares its own name and dependencies in a go.mod file. GOPATH still exists, but today it mostly just names a cache directory (downloaded module source lives under $GOPATH/pkg/mod) and a place for compiled command-line tools ($GOPATH/bin, sometimes called GOBIN). It defaults to $HOME/go on macOS and Linux, and %USERPROFILE%\go on Windows — you rarely need to touch it except to add its bin folder to your PATH.
The installer also needs to put GOROOT/bin (where the go binary lives) onto your shell’s PATH, so that typing go in any terminal finds the tool. The Windows and macOS installers do this automatically. On Linux, if you install from the official tarball rather than a package manager, you must add this to your PATH yourself — this is, by a wide margin, the most common source of “it says it’s installed but nothing works” confusion, covered in Common Mistakes below. Whenever you want to inspect exactly what your Go environment is configured with, the go env command prints every relevant setting.
Installing Go on Windows, macOS, and Linux
The general shape of installation is the same everywhere: download a release built for your OS and CPU architecture from the official downloads page, install it, and confirm the go command is on your PATH.
| Platform | Recommended method | Notes |
|---|---|---|
| Windows | Download the .msi installer from go.dev/dl and run it |
Adds go to PATH automatically; open a new Command Prompt or PowerShell window afterward |
| macOS | Download the .pkg installer from go.dev/dl, or run brew install go |
The .pkg installs to /usr/local/go and updates your PATH via /etc/paths.d |
| Linux | Download the .tar.gz archive from go.dev/dl and extract it yourself |
Most distro package repositories ship an outdated Go version, so the official tarball is usually the better choice |
The Linux tarball method is worth seeing explicitly, since it’s the one case where you do the PATH step by hand:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.3.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
rm -rf /usr/local/go— removes any previous Go install first, so files from an old version don’t linger alongside the new ones.tar -C /usr/local -xzf go1.22.3.linux-amd64.tar.gz— extracts the downloaded archive into/usr/local, which creates a fresh/usr/local/godirectory (this becomes yourGOROOT).export PATH=$PATH:/usr/local/go/bin— adds Go’sbindirectory to your shell’sPATHfor the current session. Add this same line to~/.bashrc,~/.zshrc, or your shell’s equivalent startup file so it persists across terminal sessions.
Examples
Example 1: Verifying Your Installation
Whatever method you used, the first thing to do in a fresh terminal is confirm the toolchain is actually reachable and inspect the environment it configured.
go version
go env GOROOT GOPATH
Output:
go version go1.22.3 linux/amd64
/usr/local/go
/home/you/go
go version prints the installed Go release and the OS/architecture pair it was built for. go env GOROOT GOPATH prints just those two variables (running go env alone dumps everything). If either command fails with something like “command not found,” your PATH isn’t set up yet — see Common Mistakes.
Example 2: Your First Program
Modern Go projects always start with a module. Create a directory, initialize a module inside it, and write a program:
mkdir hello && cd hello
go mod init example/hello
That command writes a go.mod file describing your module:
module example/hello
go 1.22
Now save this as hello.go in the same directory:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
Run it directly with go run hello.go. Output:
Hello, Go!
go run is a convenience command: it compiles your program and immediately executes the result, without leaving a binary behind in your project directory. It’s the fastest way to try something out while you’re learning or iterating.
Example 3: Building a Binary and Installing a Command
When you want a standalone executable instead of running through go run every time, use go build, and if you want that executable runnable by name from anywhere, use go install:
go build -o hello hello.go
./hello
go install .
hello
Output:
Hello, Go!
Hello, Go!
go build -o hello hello.go compiles a binary named hello in the current directory, which you then run directly with ./hello. go install . does the same compilation but places the resulting binary in $GOPATH/bin (your GOBIN) instead — and because that directory is on your PATH, you can now run the tool by just typing hello from any directory, exactly like a system command.
It can also be useful to check, from inside a running program, exactly which Go runtime it was built with — handy if you suspect two different Go installs are involved:
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("Go version:", runtime.Version())
fmt.Println("OS/Arch:", runtime.GOOS+"/"+runtime.GOARCH)
}
Output:
Go version: go1.22.3
OS/Arch: linux/amd64
The runtime package exposes this information at execution time, which is exactly what the compiler embedded when it built the binary — useful for confirming a deployed binary matches the toolchain you expect.
How It Works Step by Step
When you type go run hello.go, several things happen in sequence:
- The
gotool readsgo.mod(walking up from the current directory if necessary) to determine your module’s name and its declared dependencies. - It compiles
hello.goand any packages it imports, using thegccompiler. Unchanged packages are served from a local build cache instead of being recompiled, which is why repeated runs of the same code are fast. - The compiled object files are linked into a single executable, written to a temporary directory (something like
/tmp/go-buildNNNNNNon Linux). - The
gotool executes that temporary binary, streaming its standard output and standard error straight to your terminal. - Once the process exits, the temporary binary is deleted — nothing is left behind in your project folder.
go build stops after the linking step and leaves the binary sitting in your working directory (or wherever -o points) instead of deleting it. go install performs the same build, but copies the result into $GOPATH/bin so it becomes a permanent, PATH-accessible command rather than a one-off artifact.
Common Mistakes
Mistake 1: Forgetting to add Go’s bin directory to PATH. This is by far the most common install problem, especially with the Linux tarball method.
$ go version
bash: go: command not found
This happens because your shell’s PATH simply doesn’t include /usr/local/go/bin yet. Running export PATH=$PATH:/usr/local/go/bin fixes it for the current terminal session only — open a new terminal and the error comes back. The fix is to add that export line to your shell’s startup file (~/.bashrc, ~/.zshrc, or equivalent) so it’s applied every time a new shell starts, then either restart your terminal or run source ~/.bashrc.
Mistake 2: Using go get to install a command-line tool. Older tutorials often show this pattern:
go get -u golang.org/x/tools/cmd/goimports
Since Go 1.17, go get only manages the dependencies listed in your current module’s go.mod — it no longer builds and installs a binary as a side effect, and running it outside a module (or without meaning to add a dependency) either fails or does something you didn’t intend. The correct way to install a standalone command-line tool, independent of any project, is:
go install golang.org/x/tools/cmd/goimports@latest
The explicit @latest (or a pinned version like @v0.16.1) tells go install to fetch, build, and drop the compiled binary straight into $GOPATH/bin, regardless of what directory you run it from.
Mistake 3: A stale Go install shadowing the new one. If you previously installed Go via your Linux distribution’s package manager and later install a newer version from the official tarball, you can end up with two go binaries on disk. If /usr/bin (where the old package manager version lives) appears earlier in PATH than /usr/local/go/bin, go version keeps reporting the old release no matter how many times you reinstall the new one. Run which go to see exactly which binary your shell is finding, and either remove the old package (e.g. sudo apt remove golang-go) or reorder your PATH so the tarball install comes first.
Best Practices
- Install from the official downloads page (go.dev/dl) or a well-maintained package manager like Homebrew; avoid relying on older Linux distro repositories, which frequently ship Go releases that are years out of date.
- After any install or upgrade, run
go versionandgo envto confirm the toolchain and paths you expect are actually the ones active. - Start every new project with
go mod init <module-path>rather than trying to recreate an old GOPATH-style workspace — modules are the default and expected layout since Go 1.16. - Add
$(go env GOPATH)/binto yourPATHonce, up front, so any tool you install later withgo installis immediately runnable by name. - Keep Go reasonably current. Security fixes are only backported to the two most recent major releases, but Go’s strong backward-compatibility promise means upgrading almost never breaks existing code.
- On Linux, if you need a specific or very recent Go version, prefer the official tarball over your distro’s package, and remove the older package to avoid PATH conflicts.
- Use an editor with real Go tooling integration (VS Code with the Go extension, GoLand, or similar) — once
gois on yourPATH, these tools find your installation automatically and rungofmt/go vetfor you as you type.
Practice Exercises
Exercise 1: Install Go for your operating system using the method described above, then open a brand-new terminal window and run go version followed by go env GOROOT GOPATH. Confirm the version printed matches the release you downloaded.
Exercise 2: Create a new directory called greeting, initialize a module inside it with go mod init greeting, and write a hello.go that prints your name. Run it with go run hello.go, then build it into a binary with go build and run that binary directly.
Exercise 3: Use go install to install a real command-line tool, for example golang.org/x/tools/cmd/goimports@latest, then run the tool by name from a completely different directory to confirm it worked. Hint: if you get “command not found,” check the output of go env GOPATH and make sure that path’s bin subdirectory is on your PATH.
Summary
- Go installs from official binaries at go.dev/dl (or a trusted package manager) on Windows, macOS, and Linux.
GOROOTis where the Go toolchain itself lives, set automatically by the installer;GOPATHis now mainly a cache and binary-output directory used alongside each project’s owngo.mod, not a mandatory workspace root.go versionandgo envare the first commands to reach for whenever something looks misconfigured.go runcompiles and executes in one step via a temporary binary;go buildleaves a binary in place;go installplaces it in your PATH-visibleGOPATH/bin.- The most common install headaches are PATH-related: a missing bin directory, or an old installation shadowing a newer one.
- Every modern Go project starts with
go mod init— there is no need to configure a GOPATH workspace by hand anymore.
