Go: Static linking with CGO and distroless

Published: Sep 27, 2024

One of the nice things about Go is that, by default, it compiles to a statically linked binary with no external dependencies (including shared libraries). This makes it super easy to deploy, and you can use a basically empty docker image (like distroless). However, if you have CGO_ENABLED=1 (which is required for some libraries, such as the C based sqlite bindings), it will link everything statically except libc by default. When using distroless, you usually compile on an image that has libc, but then copy the binary to an image that doesn't, so while compilation doesn't fail, the binary cannot find libc and will not run.

You can remedy this by statically linking libc by passing -ldflags '-s -w -linkmode external -extldflags "-static"' to go build like so:

$ go build -a \
  -ldflags '-s -w -linkmode external -extldflags "-static"' \
  ./...

A couple things to note about this method:

  • It will take quite a bit longer to compile
  • The resulting file will be a lot larger
  • This will NOT work if you use the Go plugin system (dlopen doesn't work)

Good luck!


Filed Under: