Installing and Running Go
Overview
Setting up a Go development environment is straightforward. This chapter covers installation, verification, and the fundamental go run and go build workflow that you’ll use throughout your Go journey.
Installation
macOS
Using Homebrew (recommended):
brew install goOr download from go.dev/dl and run the installer package.
Linux
Download and extract:
# Download the latest version (check go.dev/dl for current version)
wget https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
# Remove any previous installation and extract
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz
# Add to PATH (add to ~/.bashrc or ~/.zshrc)
export PATH=$PATH:/usr/local/go/binWindows
Download the MSI installer from go.dev/dl and run it. The installer adds Go to your PATH automatically.
Verify Installation
$ go version
go version go1.26.5 darwin/arm64Environment Variables
| Variable | Purpose | Default |
|---|---|---|
GOROOT |
Go installation directory | Auto-detected |
GOPATH |
Workspace directory | $HOME/go |
GOBIN |
Binary installation directory | $GOPATH/bin |
Check your environment:
$ go env
# Shows all Go environment variables
$ go env GOPATH
/Users/yourname/goYour First Go Program
Create a file named hello.go:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}The go run Command
go run compiles and executes in one step—perfect for development:
$ go run hello.go
Hello, Go!How It Works
- Compiles the source to a temporary binary
- Executes that binary
- Cleans up the temporary file
Running Multiple Files
# Run multiple files
$ go run main.go utils.go
# Run all Go files in current directory
$ go run .The go build Command
go build creates a permanent executable:
$ go build hello.go
$ ls
hello hello.go
$ ./hello
Hello, Go!Common Build Options
# Specify output name
$ go build -o myapp hello.go
# Build for different OS/architecture
$ GOOS=linux GOARCH=amd64 go build -o myapp-linux
# Build with optimizations (strip debug info)
$ go build -ldflags="-s -w" -o myappCross-Compilation
Go makes cross-compilation trivial:
# Build for Linux from macOS
$ GOOS=linux GOARCH=amd64 go build -o app-linux
# Build for Windows
$ GOOS=windows GOARCH=amd64 go build -o app.exe
# Build for ARM (Raspberry Pi)
$ GOOS=linux GOARCH=arm GOARM=7 go build -o app-armThe Build Lifecycle
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Source Code │────▶│ Compiler │────▶│ Binary │
│ (.go) │ │ │ │ (executable)│
└─────────────┘ └─────────────┘ └─────────────┘
│
┌──────┴──────┐
│ │
┌─────▼────┐ ┌──────▼─────┐
│ go run │ │ go build │
│(temp bin)│ │(perm. bin) │
└──────────┘ └────────────┘
IDE and Editor Setup
Visual Studio Code
- Install the Go extension
- Open any
.gofile - Accept prompts to install Go tools
Recommended Tools (installed automatically)
gopls- Language serverdlv- Debuggerstaticcheck- Linter
GoLand
JetBrains GoLand is a commercial IDE with excellent Go support out of the box.
Neovim/Vim
Use gopls with your LSP client of choice (nvim-lspconfig, coc.nvim, etc.).
Project Structure Basics
myproject/
├── go.mod # Module definition
├── go.sum # Dependency checksums
├── main.go # Entry point
├── internal/ # Private packages
│ └── config/
│ └── config.go
└── pkg/ # Public packages (optional)
└── utils/
└── utils.go
Common Pitfalls
GOPATH vs Modules
Modern Go uses modules (introduced in Go 1.11). You don’t need to work inside $GOPATH/src anymore:
# Initialize a new module anywhere
$ mkdir myproject && cd myproject
$ go mod init github.com/username/myprojectExecutable vs Library
Only package main with a main() function creates executables:
// This creates a binary
package main
func main() { }// This is a library (cannot run directly)
package mylib
func Helper() { }Summary
| Command | Purpose |
|---|---|
go run |
Compile and run (temporary) |
go build |
Compile to binary |
go install |
Compile and install to $GOBIN |
go env |
Show environment variables |
go version |
Show Go version |
Exercises
- Install Go and verify with
go version - Create a “Hello, World!” program and run it with
go run - Build the same program with
go buildand execute the binary - Cross-compile for a different operating system
More examples
Example: classic Hello, World
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
fmt.Println("from go run")
}Expected:
Hello, World!
from go run
Example: exit codes and stderr
Save as main.go and go run . (with go mod init example if needed).
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: go run . <name>")
os.Exit(2)
}
fmt.Println("hello,", os.Args[1])
}Expected (with go run . gopher):
hello, gopher
Expected (with bare go run .):
usage: go run . <name>
Runnable example
Print Go runtime and build info from inside a program (complements go version / go env on the CLI).
Save as main.go. From an empty directory:
go mod init example
go run .
go build -o hello .
./hellopackage main
import (
"fmt"
"runtime"
"runtime/debug"
)
func main() {
fmt.Printf("Go version (runtime): %s\n", runtime.Version())
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Printf("NumCPU: %d\n", runtime.NumCPU())
info, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("build info: unavailable")
return
}
fmt.Printf("module path: %s\n", info.Main.Path)
fmt.Printf("go version (build): %s\n", info.GoVersion)
for _, s := range info.Settings {
switch s.Key {
case "GOOS", "GOARCH", "vcs.revision", "vcs.modified", "-compiler":
fmt.Printf("setting %s=%s\n", s.Key, s.Value)
}
}
fmt.Println("Hello from a complete Go program")
}Expected output (illustrative; values depend on your machine):
Go version (runtime): go1.22.5
OS/Arch: darwin/arm64
NumCPU: 8
module path: example
go version (build): go1.22.5
setting GOOS=darwin
setting GOARCH=arm64
setting -compiler=gc
Hello from a complete Go program
What to notice: - runtime.Version() matches the idea of go version from inside your app. - debug.ReadBuildInfo() reports the module path from go mod init. - The same source works with go run and go build. - Cross-compile targets show up as GOOS/GOARCH in build settings when set.
Try next: Build with GOOS=linux GOARCH=amd64 go build -o hello-linux . and inspect the binary name; run the program natively again and compare OS/Arch.