go.compile allows you to cross-compile Go applications to different operating systems and architectures by overriding a Go package’s build environment.
Overview
Cross-compile any Go package to Windows, macOS, Linux, or other platforms:
packages . myapp-windows = lib . go . compile {
package = pkgs . buildGoModule {
pname = "myapp" ;
version = "1.0.0" ;
src = ./. ;
vendorHash = null ;
};
goos = "windows" ;
goarch = "amd64" ;
} ;
Function Signature
go . compile : : { package , goos ?, goarch ?, ... } -> Derivation
A Go package built with buildGoModule or buildGo123Module.
goos
string
default: "stdenv.buildPlatform.go.GOOS"
Target operating system. Common values:
"linux"
"darwin" (macOS)
"windows"
"freebsd"
"openbsd"
goarch
string
default: "stdenv.buildPlatform.go.GOARCH"
Target architecture. Common values:
"amd64" (x86_64)
"arm64" (aarch64)
"386" (x86)
"arm"
Return Value
Returns a modified derivation with:
GOOS and GOARCH environment variables set
doCheck = false (tests disabled for cross-compilation)
Normalized output directory (handles Go’s $GOPATH/bin/${GOOS}_${GOARCH})
meta.mainProgram set to the binary name (with .exe for Windows)
Examples
Cross-Compile to Windows
let
myapp = pkgs . buildGoModule {
pname = "myapp" ;
version = "1.0.0" ;
src = ./. ;
vendorHash = "sha256-..." ;
};
in
{
packages = {
# Native build
default = myapp ;
# Windows 64-bit
myapp-windows = lib . go . compile {
package = myapp ;
goos = "windows" ;
goarch = "amd64" ;
};
};
}
Build for all major platforms:
{
outputs = { self , nixpkgs , nur }:
nur . lib . mkFlake { } ( system :
let
pkgs = nixpkgs . legacyPackages . ${ system } ;
lib = nur . lib . ${ system } ;
myapp = pkgs . buildGoModule {
pname = "myapp" ;
version = "1.0.0" ;
src = ./. ;
vendorHash = null ;
};
in
{
packages = {
default = myapp ;
# Linux
linux-amd64 = lib . go . compile {
package = myapp ;
goos = "linux" ;
goarch = "amd64" ;
};
linux-arm64 = lib . go . compile {
package = myapp ;
goos = "linux" ;
goarch = "arm64" ;
};
# macOS
darwin-amd64 = lib . go . compile {
package = myapp ;
goos = "darwin" ;
goarch = "amd64" ;
};
darwin-arm64 = lib . go . compile {
package = myapp ;
goos = "darwin" ;
goarch = "arm64" ;
};
# Windows
windows-amd64 = lib . go . compile {
package = myapp ;
goos = "windows" ;
goarch = "amd64" ;
};
};
}
);
}
Using with Override
Since compile is created with lib.makeOverridable, you can use .override:
let
myapp = pkgs . buildGoModule { /* ... */ };
# Create a base windows build
windowsBuild = lib . go . compile {
package = myapp ;
goos = "windows" ;
};
in
{
# Override to build for different arch
packages . windows-386 = windowsBuild . override {
goarch = "386" ;
};
}
Release Workflow
Create a release app that builds all platforms:
{
apps = lib . mkApps {
release = {
script = ''
VERSION=$(cat VERSION)
echo "Building release v$VERSION for all platforms..."
# Build all platform variants
nix build .#linux-amd64
nix build .#linux-arm64
nix build .#darwin-amd64
nix build .#darwin-arm64
nix build .#windows-amd64
# Create archives
mkdir -p dist
tar czf "dist/myapp-v$VERSION-linux-amd64.tar.gz" -C result/bin .
tar czf "dist/myapp-v$VERSION-windows-amd64.zip" -C result/bin .
echo "Release artifacts created in dist/"
'' ;
deps = with pkgs ; [ nix coreutils gnutar gzip ];
};
};
}
CGo Considerations
For pure Go code, cross-compilation works seamlessly. For CGo:
# This works for pure Go
lib . go . compile {
package = pkgs . buildGoModule { /* ... */ };
goos = "windows" ;
}
# For CGo, you may need a cross-compiler
# (Beyond the scope of go.compile - use pkgsCross instead)
Implementation Details
Binary Name Normalization
The binary name is determined from pname or name, and .exe is appended for Windows:
let
name = if ( builtins . hasAttr "pname" prev ) then prev . pname else prev . name ;
bin = if ( goos == "windows" ) then " ${ name } .exe" else name ;
in
Output Directory Normalization
Go’s build system puts cross-compiled binaries in $GOPATH/bin/${GOOS}_${GOARCH}. The postBuild phase normalizes this:
dir = $GOPATH /bin/ ${ GOOS } _ ${ GOARCH }
if [[ -n "$( shopt -s nullglob; echo $dir / * )" ]]; then
mv $dir / * $dir /..
fi
if [[ -d $dir ]]; then
rmdir $dir
fi
Test Disabling
Tests are automatically disabled for cross-compilation:
Source Location
Implemented in: libs/go/compile.nix:6
GOOS and GOARCH Values
Supported GOOS Values
aix
android
darwin (macOS)
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
windows
Supported GOARCH Values
386 (32-bit x86)
amd64 (64-bit x86)
arm (32-bit ARM)
arm64 (64-bit ARM)
loong64
mips
mips64
mips64le
mipsle
ppc64
ppc64le
riscv64
s390x
wasm
Not all combinations are valid. See Go’s supported platforms for the compatibility matrix.
Best Practices
Test cross-compiled binaries : Run them on target platforms to verify
Avoid CGo for cross-compilation : Pure Go is much simpler
Use meaningful package names : Include platform in the name (e.g., myapp-windows)
Disable tests : They often don’t work when cross-compiling
Rust Utilities Cross-compile Rust with cargo-zigbuild
Deno Utilities Compile TypeScript/JavaScript to binaries