Updated on 2023-05-24: Added information on cross-compiling using the MUSL and GNU toolchain on macOS for static linking.
Introduction #
With the release of Go v1.5, it became possible to cross-compile applications for various targets such as Windows, macOS, Linux, Solaris, and Android. Cross-compilation allows you to build binaries for different operating systems from a single development machine. This article explains how to cross-compile Go applications, including those that use Cgo for C dependencies.
Supported Platforms #
Go supports cross-compilation to many platforms. Below is a table of the supported GOOS and GOARCH combinations as of Go version 1.22.2:
Each entry represents a GOOS and GOARCH environment variable combination. The first one for example, aix/ppc64, would be defined as GOOS=aix GOARCH=ppc64 before the actual compile command.
> go tool dist list | column -c 35 | column -t
aix/ppc64 linux/mips64le
android/386 linux/mipsle
android/amd64 linux/ppc64
android/arm linux/ppc64le
android/arm64 linux/riscv64
darwin/amd64 linux/s390x
darwin/arm64 netbsd/386
dragonfly/amd64 netbsd/amd64
freebsd/386 netbsd/arm
freebsd/amd64 netbsd/arm64
freebsd/arm openbsd/386
freebsd/arm64 openbsd/amd64
freebsd/riscv64 openbsd/arm
illumos/amd64 openbsd/arm64
ios/amd64 openbsd/ppc64
ios/arm64 plan9/386
js/wasm plan9/amd64
linux/386 plan9/arm
linux/amd64 solaris/amd64
linux/arm wasip1/wasm
linux/arm64 windows/386
linux/loong64 windows/amd64
linux/mips windows/arm
linux/mips64 windows/arm64
Cross-compile examples #
Windows #
# 32 bit
GOOS=windows GOARCH=386 go build
# 64 bit
GOOS=windows GOARCH=amd64 go build
# arm
GOOS=windows GOARCH=arm go build
# arm 64 bit
GOOS=windows GOARCH=arm64 go build
macOS #
# 64 bit
GOOS=darwin GOARCH=amd64 go build
# arm 64 bit
GOOS=darwin GOARCH=arm64 go build
Linux #
# 32 bit
GOOS=linux GOARCH=386 go build
# 64 bit
GOOS=linux GOARCH=amd64 go build
# arm
GOOS=linux GOARCH=arm go build
# arm 64 bit
GOOS=linux GOARCH=arm64 go build
Cross-compile with Cgo #
Some dependencies, such as SQLite, require Cgo instead of just pure Go to compile.
The following examples cover both static and dynamic linking.
Static Linking Static linking ensures that the resulting binary includes all necessary dependencies and does not require any libraries on the target system. This is useful for creating self-contained binaries, especially for small images like Alpine.
Dynamic Linking Dynamic linking means that the resulting binary relies on shared libraries that must be present on the target system.
macOS to macOS #
# 64 bit static
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -ldflags="-extldflags=-static"
# 64 bit dynamic
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build
# arm64 bit static
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -ldflags="-extldflags=-static"
# arm64 bit dynamic
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build
macOS to Linux #
MUSL: Static or Dynamic Linking #
MUSL-based systems are, for example, Alpine.
Installing musl gcc enables cross-compilation.
In combination with static binaries, the binary will run on any Linux distribution. Non-static binaries only run on musl-based systems like Alpine.
brew install filosottile/musl-cross/musl-cross
The following command will build a static binary, which allows it to run on any linux distribution, see here for more information
# 64 bit static
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ go build -ldflags="-extldflags=-static"
# 64 bit dynamic
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ go build
# arm64 static
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-musl-gcc CXX=aarch64-linux-musl-g++ go build -ldflags="-extldflags=-static"
# arm64 dynamic
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-musl-gcc CXX=aarch64-linux-musl-g++ go build
GNU: Dynamic Linking preferred #
Install the GNU toolchain:
brew tap messense/macos-cross-toolchains
# x64 / x86
brew install messense/macos-cross-toolchains/x86_64-unknown-linux-gnu
# arm64
brew install messense/macos-cross-toolchains/aarch64-unknown-linux-gnu
Building the application using the GNU toolchain:
# 64 bit static
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ go build -ldflags="-linkmode external -extldflags -static"
# 64 bit dynamic
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ go build
# arm64 static
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ go build -ldflags="-linkmode external -extldflags -static"
# arm64 dynamic
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ go build
macOS to Windows #
At first, make sure to have Windows compiler setup, such as MinGW.
brew install mingw-w64
Then compile:
# 32 bit
GOOS=windows GOARCH=386 CGO_ENABLED=1 CC="i686-w64-mingw32-gcc" go build
# 64 bit
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC="x86_64-w64-mingw32-gcc" go build
For more information regarding Cgo, please follow up here .