Skip to main content
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 NameTarget PlatformGOOSGOARCH
go-x86_64-linuxx86_64 Linuxlinuxamd64
go-aarch64-linuxARM64 Linuxlinuxarm64
go-arm-linuxARM Linuxlinuxarm
go-x86_64-darwinIntel macOSdarwinamd64
go-aarch64-darwinApple Silicon macOSdarwinarm64
go-x86_64-windowsx86_64 Windowswindowsamd64

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}):
  1. Sets environment - Configures GOOS and GOARCH for target platform
  2. Cross-compiles - Builds using Go’s native cross-compilation
  3. Normalizes output - Moves binaries from $GOPATH/bin/{GOOS}_{GOARCH}/ to $GOPATH/bin/
  4. 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

Build for All Platforms

#!/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

Performance Comparison

Binary Sizes

Example Go HTTP server (minimal dependencies):
PlatformStandardCompressedReduction
x86_64-linux6.2 MB2.1 MB66%
aarch64-darwin6.5 MB2.3 MB65%
x86_64-windows6.4 MB2.2 MB66%
Actual sizes depend on your application and dependencies. CGo-enabled builds may be larger.

Build Times

On a modern system:
Bundler TypeSmall AppMedium AppLarge App
Standard5-10s15-30s30-60s
Compressed10-20s25-45s45-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.

Custom Build Tags

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

Supported Platforms

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

exec format error
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

Build docs developers (and LLMs) love