Post

Cross-Building Go Programs with golang-crossbuild

Cross-Building Go Programs with golang-crossbuild

When developing Go applications intended to run on multiple architectures (like ARM devices from an x86 machine), you need a reliable and reproducible way to cross-compile. In this post, I’ll share how I use the docker.elastic.co/beats-dev/golang-crossbuild Docker image along with qemu-user-static to build Go programs for ARM from my x86_64 development environment.

This guide is ideal for developers targeting Raspberry Pi or ARM-based IoT devices, especially when using CI/CD environments or containers for builds.


🧰 Why Cross-Compiling with Docker?

Cross-compiling means building binaries for a different architecture than the host machine. While Go supports cross-compilation natively with environment variables like GOARCH and GOOS, Docker-based cross-compilation offers several advantages:

  • βœ… Consistent build environments
  • βœ… Easy integration with CI pipelines
  • βœ… Preinstalled cross-compilation toolchains
  • βœ… No need to pollute your host environment

🐳 Using golang-crossbuild from Elastic

Elastic maintains a powerful Docker image at docker.elastic.co/beats-dev/golang-crossbuild. It’s preconfigured with Go and cross-compilers for various architectures.

πŸ› οΈ Basic Usage

To compile a Go binary for linux/arm64, use a command like:

1
2
3
4
5
6
docker run --rm \
  -v "$(pwd)":/go/src/github.com/your/repo \
  -w /go/src/github.com/your/repo \
  docker.elastic.co/beats-dev/golang-crossbuild:1.22.10-darwin-arm64-debian11 \
  --build-cmd "go build -o build/your-binary-linux-armv7 ./cmd/yourapp" \
  -p "darwin/arm64"

πŸ“Œ Explanation:

  • -v "$(pwd)"...: Mounts your current directory into the container.
  • -w: Sets the working directory inside the container.
  • The image tag 1.22.10-darwin-arm64-debian11 specifies the target architecture.
  • --build-cmd: The actual build command to run inside.
  • -p: sets the target architecture

🧩 What About QEMU?

To emulate ARM on x86, you’ll need to register QEMU emulation:

βš™οΈ Installing and Registering QEMU

1
2
sudo apt-get install qemu-user-static
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

This allows the container to run ARM binaries using QEMU, which is especially useful when you’re building and testing in one pipeline.

Tip: You only need to register QEMU once per system reboot.


πŸ“¦ Organizing Your Project

For best results, structure your Go project like this:

1
2
3
4
5
6
7
8
πŸ“ your-go-app/
β”œβ”€β”€ cmd/
β”‚   └── yourapp/
β”‚       └── main.go
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
└── build/
    └── (output binaries)

This layout plays nicely with most build scripts and keeps things clean.


πŸ“‹ Example: Build Script

Here’s a sample shell script that automates the build:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
set -e

REPO_PATH="github.com/your/repo"
OUT_DIR="./build"

mkdir -p "$OUT_DIR"

docker run --rm \
  -v "$(pwd)":/go/src/$REPO_PATH \
  -w /go/src/$REPO_PATH \
  docker.elastic.co/beats-dev/golang-crossbuild:1.22.10-darwin-arm64-debian11 \
  --build-cmd "go build -o $OUT_DIR/yourapp-linux-arm ./cmd/yourapp" \
  -p "darwin/arm64"

Give it execute permissions with chmod +x build.sh, then run ./build.sh to compile.


πŸ§ͺ Testing the Build on ARM Device

After the binary is built, copy it to your ARM device:

1
scp build/yourapp-linux-arm pi@raspberrypi.local:/home/pi/

Then SSH into the device and run:

1
2
chmod +x yourapp-linux-arm
./yourapp-linux-arm

If everything is set up right, it should work immediately.


🧠 Final Thoughts

Cross-building Go programs with Docker and the Beats Crossbuild image makes targeting different architectures clean and reproducible. Using qemu-user-static fills the last gap by allowing ARM binaries to be run or tested directly from an x86 machine or container.

If you’re developing software for embedded systems, Raspberry Pi, or distributing binaries across platforms, this workflow is a strong foundation.

Happy hacking!

This post is licensed under CC BY 4.0 by the author.