The Go bundlers enable cross-compilation of Go applications to multiple platforms, with optional UPX compression for minimal binary sizes.
Available Bundlers
Go bundlers come in two variants across 6 target platforms:
Standard Bundlers
Bundler Name Target Platform GOOS GOARCH go-x86_64-linuxx86_64 Linux linux amd64 go-aarch64-linuxARM64 Linux linux arm64 go-arm-linuxARM Linux linux arm go-x86_64-darwinIntel macOS darwin amd64 go-aarch64-darwinApple Silicon macOS darwin arm64 go-x86_64-windowsx86_64 Windows windows amd64
Compressed Bundlers
Same platforms with UPX compression:
go-compress-x86_64-linux
go-compress-aarch64-linux
go-compress-arm-linux
go-compress-x86_64-darwin
go-compress-aarch64-darwin
go-compress-x86_64-windows
Compressed bundlers reduce binary size by 50-70% using UPX with --best --lzma flags.
How They Work
Standard Go Bundler
The standard bundler (go-{system}):
Sets environment - Configures GOOS and GOARCH for target platform
Cross-compiles - Builds using Go’s native cross-compilation
Normalizes output - Moves binaries from $GOPATH/bin/{GOOS}_{GOARCH}/ to $GOPATH/bin/
Names binary - Adds .exe extension for Windows
From bundlers/go/default.nix:15-32:
# build for specific GOOS and GOARCH
env = previousAttrs . env // {
GOOS = goos ;
GOARCH = goarch ;
} ;
doCheck = false ;
# normalize cross-compiled builds
postBuild = ''
dir=$GOPATH/bin/ ''$ {GOOS}_ ''$ {GOARCH}
if [[ -n "$(shopt -s nullglob; echo $dir/*)" ]]; then
mv $dir/* $dir/..
fi
if [[ -d $dir ]]; then
rmdir $dir
fi
'' ;
Compressed Go Bundler
The compressed bundler (go-compress-{system}) adds UPX compression:
From bundlers/go/compress.nix:35-48:
nativeBuildInputs = previousAttrs . nativeBuildInputs ++ [ pkgs . upx ] ;
# compress binary
postInstall = ''
FILE=$(find " ''$ {out}" -type f -print -quit)
TMP_FILE=" ''$ {TMPDIR:-/tmp}/bin"
mv " ''$ {FILE}" " ''$ {TMP_FILE}"
rm -rf " ''$ {out}"
upx --best --lzma " ''$ {TMP_FILE}" || true
cat " ''$ {TMP_FILE}" > " ''$ {out}"
chmod +x " ''$ {out}"
'' ;
Basic Usage
Cross-Compile for Linux
nix bundle \
--bundler github:nurpkgs/nur-nix#go-x86_64-linux \
.#my-go-app
Result : /nix/store/...-my-go-app-x86_64-linux/bin/my-go-app
Cross-Compile for macOS with Compression
nix bundle \
--bundler github:nurpkgs/nur-nix#go-compress-aarch64-darwin \
.#my-go-app
Result : Compressed macOS binary (50-70% smaller)
Cross-Compile for Windows
nix bundle \
--bundler github:nurpkgs/nur-nix#go-x86_64-windows \
.#my-go-app
Result : /nix/store/...-my-go-app-x86_64-windows/bin/my-go-app.exe
Complete Workflow
#!/usr/bin/env bash
PACKAGE = ".#my-go-app"
OUTPUT_DIR = "./dist"
mkdir -p " $OUTPUT_DIR "
# Standard builds
PLATFORMS = (
"x86_64-linux"
"aarch64-linux"
"arm-linux"
"x86_64-darwin"
"aarch64-darwin"
"x86_64-windows"
)
for platform in "${ PLATFORMS [ @ ]}" ; do
echo "Building for $platform ..."
result = $( nix bundle \
--bundler "github:nurpkgs/nur-nix#go- $platform " \
--print-out-paths \
" $PACKAGE " )
# Copy binary to dist directory
binary = $( find " $result /bin" -type f -executable )
cp " $binary " " $OUTPUT_DIR /$( basename $binary )"
echo "Saved to $OUTPUT_DIR /$( basename $binary )"
done
Build Compressed Binaries Only
#!/usr/bin/env bash
PACKAGE = ".#my-go-app"
for platform in x86_64-linux aarch64-linux x86_64-windows ; do
echo "Building compressed binary for $platform ..."
nix bundle \
--bundler "github:nurpkgs/nur-nix#go-compress- $platform " \
" $PACKAGE "
done
Binary Sizes
Example Go HTTP server (minimal dependencies):
Platform Standard Compressed Reduction x86_64-linux 6.2 MB 2.1 MB 66% aarch64-darwin 6.5 MB 2.3 MB 65% x86_64-windows 6.4 MB 2.2 MB 66%
Actual sizes depend on your application and dependencies. CGo-enabled builds may be larger.
Build Times
On a modern system:
Bundler Type Small App Medium App Large App Standard 5-10s 15-30s 30-60s Compressed 10-20s 25-45s 45-90s
Compression adds ~2-5x overhead but drastically reduces distribution size.
Advanced Usage
CGo Cross-Compilation
Go bundlers disable checks (doCheck = false) but support CGo:
# flake.nix
{
outputs = { self , nixpkgs }:
let
system = "x86_64-linux" ;
pkgs = nixpkgs . legacyPackages . ${ system } ;
in
{
packages . ${ system } . my-cgo-app = pkgs . buildGoModule {
pname = "my-cgo-app" ;
version = "1.0.0" ;
src = ./. ;
vendorHash = "sha256-..." ;
# CGo enabled
CGO_ENABLED = 1 ;
};
};
}
CGo cross-compilation requires platform-specific C toolchains. Standard Go bundlers work best with pure Go code.
Override the bundler to add build tags:
{
description = "Custom Go bundler with tags" ;
inputs = {
nixpkgs . url = "github:NixOS/nixpkgs/nixos-unstable" ;
};
outputs = { self , nixpkgs }:
let
system = "x86_64-linux" ;
pkgs = nixpkgs . legacyPackages . ${ system } ;
in
{
bundlers . ${ system } . go-custom = drv :
drv . overrideAttrs ( prev : {
env = prev . env // {
GOOS = "linux" ;
GOARCH = "amd64" ;
};
buildFlags = [ "-tags" "production,netgo" ];
});
};
}
Static Linking
Create fully static binaries:
{
packages . ${ system } . my-static-app = pkgs . buildGoModule {
pname = "my-static-app" ;
version = "1.0.0" ;
src = ./. ;
vendorHash = "sha256-..." ;
CGO_ENABLED = 0 ;
ldflags = [
"-s"
"-w"
"-extldflags=-static"
];
};
}
Then bundle:
nix bundle \
--bundler github:nurpkgs/nur-nix#go-compress-x86_64-linux \
.#my-static-app
From bundlers/go/all.nix:3-35, the bundler supports:
targets = [
{
goos = "windows" ;
goarch = "amd64" ;
normalized = "x86_64-windows" ;
}
{
goos = "darwin" ;
goarch = "amd64" ;
normalized = "x86_64-darwin" ;
}
{
goos = "darwin" ;
goarch = "arm64" ;
normalized = "aarch64-darwin" ;
}
{
goos = "linux" ;
goarch = "amd64" ;
normalized = "x86_64-linux" ;
}
{
goos = "linux" ;
goarch = "arm" ;
normalized = "arm-linux" ;
}
{
goos = "linux" ;
goarch = "arm64" ;
normalized = "aarch64-linux" ;
}
] ;
Troubleshooting
Binary Won’t Run on Target
Cause : Wrong architecture or OS
Solution : Verify target platform matches:
file /nix/store/...-my-go-app-x86_64-linux/bin/my-go-app
# ELF 64-bit LSB executable, x86-64
UPX Compression Fails
upx: my-go-app: NotCompressibleException
Cause : Binary already compressed or doesn’t benefit from UPX
Solution : The bundler continues with || true, so this is non-fatal. Use standard bundler if needed.
Large Binary After Compression
Compressed binary is still large.
Solution : Check dependencies:
go mod graph | grep -v indirect
Remove unused dependencies and rebuild.
Windows Binary Missing .exe Extension
Cause : Bundler should automatically add .exe
Solution : Verify the bundler detected Windows:
binName = if goos == "windows" then " ${ previousAttrs . pname } .exe" else previousAttrs . pname ;
Best Practices
When to Use Compression
Use compressed bundlers :
Distribution over networks
Storage-constrained environments
Downloads for end users
Embedded systems
Use standard bundlers :
Development builds
When startup time is critical
Debugging (UPX may interfere)
Platforms where UPX isn’t well-supported
Distribution Strategy
#!/usr/bin/env bash
# Build and release script
VERSION = "1.0.0"
APP_NAME = "my-go-app"
OUTPUT = "./releases/v $VERSION "
mkdir -p " $OUTPUT "
# Build compressed binaries for all platforms
for platform in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin x86_64-windows ; do
echo "Building $APP_NAME v $VERSION for $platform ..."
result = $( nix bundle \
--bundler "github:nurpkgs/nur-nix#go-compress- $platform " \
--print-out-paths \
".# $APP_NAME " )
binary = $( find " $result /bin" -type f -executable )
output_name = " $APP_NAME -v $VERSION - $platform "
cp " $binary " " $OUTPUT / $output_name "
# Create checksums
( cd " $OUTPUT " && sha256sum " $output_name " > " $output_name .sha256" )
done
echo "Release artifacts in $OUTPUT "
Deno Bundlers Cross-compile TypeScript/JavaScript
Docker Bundlers Containerize Go applications