Cross-Platform Compilation
One of the best features of Go language is the ability to produce a binary for any supported target platform from
any host. You can create a binary for Windows operating system 1 and arm64 processors when running building process on
completely different platform than windows itself. This is a crucial feature that allows us to create reproducible
builds 2.
A reproducible build is a concept in software development where compiling the same source code—with the same tools and environment—always produces identical output files (bit-for-bit), no matter who builds it or where. In Go language we don’t need to install any additional tools to use cross-compilation feature. Building process is controlled through these two environment variables:
GOOS: The operating system for which to compile code. Example:darwin.GOARCH: The architecture, or processor, for which to compile code. Example:arm64.
We can see all the supported combinations of operating systems and architectures by running this command:
$ go version
go version go1.26.1-X:nodwarf5 linux/amd64
$ go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/loong64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
openbsd/ppc64
openbsd/riscv64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
wasip1/wasm
windows/386
windows/amd64
windows/arm64
To compile a binary for linux operating system and arm64 platform set appropirate values for two environment variables:
$ GOOS=linux GOARCH=arm64 go build -v -o app ./cmd/app
internal/goos
internal/goexperiment
internal/godebugs
internal/coverage/rtcov
(...)
$ file app | tr , '\n'
app: ELF 64-bit LSB executable
ARM aarch64
version 1 (SYSV)
statically linked
(...)
The flag -v will print the names of packages as they are compiled. By using the command file we can verify that the
binary was produced for ARM aarch64. In the process of cross compilation the Go will rebuild standard library for the
target every time. That means that the cross-compilation will take a little longer than compilation for a native platform.
For the cross-compilation process there are bunch of additional environment variables that control how the binary will
be produced. For example when You use OSARCH=amd64 You can also tweak the GOAMD=v1 variable. Here is a list of these:
GOAMD64: For GOARCH=amd64, the microarchitecture level for which to compile 3. Valid values are v1 (default), v2, v3, v4.GOARM: For GOARCH=arm, the ARM architecture for which to compile. Valid values are 5, 6, 7 4.GOARM64: For GOARCH=arm64, the ARM64 architecture for which to compile. Valid values are v8.0 (default), v8.{1-9}, v9.{0-5} 5.GOWASM: For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use 6.
For more information refer to man pages: go help environment 7.
There are also two environment variables that are purely informational – they hold information about our own machine:
GOHOSTARCH: The architecture (GOARCH) of the Go toolchain binaries.GOHOSTOS: The operating system (GOOS) of the Go toolchain binaries.
This cross-compilation approach works fine when we have a project that does not depend on any C libraries. The CGO
(CGO_ENABLED=0). In this case we will Go will produce a statically linked binary. The process becomes more complicated
when we do use CGO. But it’s a story for another time.
Conclusion
Go language cross-compilation feature lets you build a single, static binary for any operating system and architecture
by simply setting the GOOS and GOARCH environment variables. Because it includes all dependencies, you can run the
resulting file on the target platform without installing Go or matching system libraries.