How does cross-compilation work in Go?
Quick Answer
Go supports cross-compilation natively, with no separate toolchain or emulator needed, through the GOOS and GOARCH environment variables: GOOS=linux GOARCH=amd64 go build produces a Linux/x86-64 binary even when run from a Mac or Windows machine. This works because the Go toolchain ships pre-built standard library packages for every supported OS/architecture combination, and the compiler itself is capable of targeting any of them directly. This is dramatically simpler than cross-compilation in languages like C/C++, which typically require a separate cross-compiler toolchain and target-specific system headers/libraries to be installed and configured for each target platform.
Detailed Answer
Cross-compiling a Go program is often just setting two environment variables, no extra tools required.
Basic cross-compilation
GOOS=linux GOARCH=amd64 go build -o myapp-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -o myapp-mac-arm64 .
GOOS=windows GOARCH=amd64 go build -o myapp-windows-amd64.exe .
Each of these produces a native, statically-linked-by-default binary for its target platform, buildable from any single development machine.
Listing supported combinations
go tool dist list
darwin/amd64
darwin/arm64
linux/386
linux/amd64
linux/arm64
windows/amd64
...dozens more...
Why this is simpler than in C/C++
C/C++ cross-compilation typically needs:
- A separate cross-compiler toolchain per target (e.g., a Linux-hosted
compiler that targets ARM)
- Target-specific system headers and libraries installed
- Careful handling of dynamic linking against the target's C library
Go cross-compilation needs:
- GOOS and GOARCH set
- That's it, in the common case
Go's standard library ships pre-compiled for every supported platform combination, and Go binaries are statically linked by default (aside from cgo-using code), so there's no target-side system library dependency to manage in the typical case.
The cgo caveat
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .
If your code uses cgo (calling into C libraries), cross-compilation gets much harder, since it genuinely needs a C cross-compiler for the target platform. Setting CGO_ENABLED=0 disables cgo entirely, forcing a pure-Go build, which is why minimal container images for Go services often explicitly set this to guarantee simple, dependency-free cross-compilation.