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/go directory (this becomes your GOROOT).
  • export PATH=$PATH:/usr/local/go/bin — adds Go’s bin directory to your shell’s PATH for 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 go tool reads go.mod (walking up from the current directory if necessary) to determine your module’s name and its declared dependencies.
  • It compiles hello.go and any packages it imports, using the gc compiler. 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-buildNNNNNN on Linux).
  • The go tool 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 version and go env to 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)/bin to your PATH once, up front, so any tool you install later with go install is 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 go is on your PATH, these tools find your installation automatically and run gofmt/go vet for 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.
  • GOROOT is where the Go toolchain itself lives, set automatically by the installer; GOPATH is now mainly a cache and binary-output directory used alongside each project’s own go.mod, not a mandatory workspace root.
  • go version and go env are the first commands to reach for whenever something looks misconfigured.
  • go run compiles and executes in one step via a temporary binary; go build leaves a binary in place; go install places it in your PATH-visible GOPATH/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.