Back to Blog
Engineering February 11, 2026

Solving Cross-Platform CGO Builds with Zig and Dagger

john
john CTO

Cross-compiling pure Go is easy. Set GOOS and GOARCH, run go build, and you’re done. But the moment you introduce CGO—C bindings for libraries like sqlite or sqlite-vec—the whole thing falls apart. Wrong headers, missing cross-compilers, platform-specific toolchains that only work on one CI runner. It gets ugly fast.

This is the problem I ran into building tapes. We needed to ship binaries for Linux amd64, Linux arm64, macOS amd64, and macOS arm64—all with sqlite baked in via CGO. For the full walkthrough with code examples, read my detailed post.

“If your CI pipeline needs a different machine per target, you don’t have a build system—you have a collection of workarounds.”

Zig as a Drop-in Compiler

The first breakthrough was using Zig as a universal C/C++ cross-compiler. Zig ships with a built-in cc command that targets any architecture out of the box—no extra toolchains, no platform-specific headers. You point Go at it and everything just works:

CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \
  CC="zig cc -target aarch64-linux-gnu" \
  CXX="zig c++ -target aarch64-linux-gnu" \
  go build -o build main.go

One compiler. Every Linux target. No matrix of bespoke cross-compilation setups.

Dagger for Reproducible Builds

The second piece was Dagger—a container-based CI tool where pipelines are written in Go instead of YAML. We define build targets as structs, iterate over them in a containerized environment with Zig installed, and export the resulting binaries. The entire build is reproducible, cached, and runs identically on a laptop or in CI.

type buildTarget struct {
    goos, goarch, cc, cxx, cgoFlags, cgoLdFlags string
}

No more debugging why a build works locally but breaks in GitHub Actions. The container is the source of truth.

macOS Targets with osxcross

There’s one catch Zig can’t solve on its own: macOS. Apple doesn’t freely distribute the system libraries you need for Darwin targets, so Linux-to-macOS cross-compilation fails even with Zig. The pragmatic answer is osxcross, which packages the macOS SDK into a cross-compilation toolchain that runs on Linux.

The Result

What used to require a matrix of CI runners and fragile platform-specific scripts is now a single portable build that produces all four target binaries. Zig handles Linux, osxcross handles macOS, and Dagger makes the whole thing reproducible.

The full Dagger modules are in the tapes repo. Read the complete post for all the code and implementation details.

Stay Updated

Get updates on open source and distributed systems in AI infrastructure.

Previous Post

Introducing tapes: Transparent Telemetry for AI Agents

Next Post

Continuous AI: How We Keep Our tapes.dev Docs Current