Docker container to turn CGO cross-compilation pain into a pleasure. It tested on variety of platforms. Custom sysroots also can be used.
Tip! Should you wish to see working examples instead of reading
This project is rather a cookbook combining various projects into one. Special thanks to osxcross for an amazing cross-compile environment for OSX.
Docker images are available on both GitHub and Docker hub. The image's tag version follows Golang
release so if you use 1.19.4, you are effectively compiling your Go code using Golang at version 1.19.4. The actual version of the other tools installed within the Docker image like goreleaser can
be found in the latest releases.
Images from version v1.17.4 are multi-arch. Supported hosts are listed in the table below.
| Host | Supported | CC is set to | CXX is set to |
|---|---|---|---|
| amd64 | ✅ | x86_64-linux-gnu-gcc | x86_64-linux-gnu-g++ |
| arm64 (aka aarch64) | ✅ | aarch64-linux-gnu-gcc | aarch64-linux-gnu-gcc |
Below are additional environment variables to set when cross compiling with CGO.
| Env variable | Value | Required | Notes |
|---|---|---|---|
CGO_ENABLED | 1 | Yes | Instead of specifying it in each build it can be set globally during docker run -e CGO_ENABLED=1 |
CC | see targets | Optional | |
CXX | see targets | Optional | |
PKG_CONFIG_SYSROOT_DIR | Required if sysroot is present | ||
PKG_CONFIG_PATH | Optional | List of directories containing pkg-config files |
-I and -L to use the directories located in target's sysroot.PKG_CONFIG_SYSROOT_DIR is prefixed to -I and -L. For instance -I/usr/include/libfoo becomes -I/var/target/usr/include/libfoo
with a PKG_CONFIG_SYSROOT_DIR set to /var/target (same rule apply to -L).pc files.| Platform | Arch | CC | CXX | Verified |
|---|---|---|---|---|
| Darwin | amd64 | o64-clang | o64-clang++ | ✅ |
| Darwin (M1/M2) | arm64 | oa64-clang | oa64-clang++ | ✅ |
| Linux | amd64 | x86_64-linux-gnu-gcc | x86_64-linux-gnu-g++ | ✅ |
| Linux | arm64 | aarch64-linux-gnu-gcc | aarch64-linux-gnu-g++ | ✅ |
| Linux | armhf (GOARM=5) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | Verification required |
| Linux | armhf (GOARM=6) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | Verification required |
| Linux | armhf (GOARM=7) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | ✅ |
| Linux | s390x | s390x-linux-gnu-gcc | s390x-linux-gnu-g++ | ✅ |
| Windows | amd64 | x86_64-w64-mingw32-gcc | x86_64-w64-mingw32-g++ | ✅ |
| Windows | arm64 | /llvm-mingw/bin/aarch64-w64-mingw32-gcc | /llvm-mingw/bin/aarch64-w64-mingw32-g++ | ✅ |
GPG_KEY (optional) - defaults to /secrets/key.gpg. ignored if file not foundDOCKER_CREDS_FILE (optional) - path to JSON file with docker login credentials. Useful when push to multiple docker registries requiredDOCKER_FAIL_ON_LOGIN_ERROR (optional) - fail on docker login errorUse docker login to auth to repos and mount docker config file. For example:
docker run -v $(HOME)/.docker/config.json:/root/.docker/config.json ...
To login from within goreleaser-cross container create creds file.
{
"registries": [
{
"user": "<username>",
"pass": "<password>",
"registry": "<registry url>" // for example ghcr.io
}
]
}
Most reasonable way to make a sysroot seem to be rsync and the example is using it. You may want to use the script to create sysroot for your desired setup. Lets consider creating sysroot for Raspberry Pi 4 running Debian Buster.
./sysroot-rsync.sh pi@<ipaddress> <local destination>
Though sshfs is a good way to test sysroot before running rsync it introduces cons. Some packages are creating absolute links and thus pointing to wrong files when mounted (
or appear as broken). For example RPI4 running Debian Buster the library /usr/lib/x86_x64-gnu-linux/libpthread.so is symlink to /lib/x86_x64-gnu-linux/libpthread.so instead
of ../../../lib/x86_x64-gnu-linux/libpthread.so.
Any contribution helping to make this project is welcome
276 followers · starred Apr 2024
841 followers · starred Jul 2022
701 followers · starred Jan 2023
860 followers · starred Feb 2026
Docker container to turn CGO cross-compilation pain into a pleasure. It tested on variety of platforms. Custom sysroots also can be used.
Tip! Should you wish to see working examples instead of reading
This project is rather a cookbook combining various projects into one. Special thanks to osxcross for an amazing cross-compile environment for OSX.
Docker images are available on both GitHub and Docker hub. The image's tag version follows Golang
release so if you use 1.19.4, you are effectively compiling your Go code using Golang at version 1.19.4. The actual version of the other tools installed within the Docker image like goreleaser can
be found in the latest releases.
Images from version v1.17.4 are multi-arch. Supported hosts are listed in the table below.
| Host | Supported | CC is set to | CXX is set to |
|---|---|---|---|
| amd64 | ✅ | x86_64-linux-gnu-gcc | x86_64-linux-gnu-g++ |
| arm64 (aka aarch64) | ✅ | aarch64-linux-gnu-gcc | aarch64-linux-gnu-gcc |
Below are additional environment variables to set when cross compiling with CGO.
| Env variable | Value | Required | Notes |
|---|---|---|---|
CGO_ENABLED | 1 | Yes | Instead of specifying it in each build it can be set globally during docker run -e CGO_ENABLED=1 |
CC | see targets | Optional | |
CXX | see targets | Optional | |
PKG_CONFIG_SYSROOT_DIR | Required if sysroot is present | ||
PKG_CONFIG_PATH | Optional | List of directories containing pkg-config files |
-I and -L to use the directories located in target's sysroot.PKG_CONFIG_SYSROOT_DIR is prefixed to -I and -L. For instance -I/usr/include/libfoo becomes -I/var/target/usr/include/libfoo
with a PKG_CONFIG_SYSROOT_DIR set to /var/target (same rule apply to -L).pc files.| Platform | Arch | CC | CXX | Verified |
|---|---|---|---|---|
| Darwin | amd64 | o64-clang | o64-clang++ | ✅ |
| Darwin (M1/M2) | arm64 | oa64-clang | oa64-clang++ | ✅ |
| Linux | amd64 | x86_64-linux-gnu-gcc | x86_64-linux-gnu-g++ | ✅ |
| Linux | arm64 | aarch64-linux-gnu-gcc | aarch64-linux-gnu-g++ | ✅ |
| Linux | armhf (GOARM=5) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | Verification required |
| Linux | armhf (GOARM=6) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | Verification required |
| Linux | armhf (GOARM=7) | arm-linux-gnueabihf-gcc | arm-linux-gnueabihf-g++ | ✅ |
| Linux | s390x | s390x-linux-gnu-gcc | s390x-linux-gnu-g++ | ✅ |
| Windows | amd64 | x86_64-w64-mingw32-gcc | x86_64-w64-mingw32-g++ | ✅ |
| Windows | arm64 | /llvm-mingw/bin/aarch64-w64-mingw32-gcc | /llvm-mingw/bin/aarch64-w64-mingw32-g++ | ✅ |
GPG_KEY (optional) - defaults to /secrets/key.gpg. ignored if file not foundDOCKER_CREDS_FILE (optional) - path to JSON file with docker login credentials. Useful when push to multiple docker registries requiredDOCKER_FAIL_ON_LOGIN_ERROR (optional) - fail on docker login errorUse docker login to auth to repos and mount docker config file. For example:
docker run -v $(HOME)/.docker/config.json:/root/.docker/config.json ...
To login from within goreleaser-cross container create creds file.
{
"registries": [
{
"user": "<username>",
"pass": "<password>",
"registry": "<registry url>" // for example ghcr.io
}
]
}
Most reasonable way to make a sysroot seem to be rsync and the example is using it. You may want to use the script to create sysroot for your desired setup. Lets consider creating sysroot for Raspberry Pi 4 running Debian Buster.
./sysroot-rsync.sh pi@<ipaddress> <local destination>
Though sshfs is a good way to test sysroot before running rsync it introduces cons. Some packages are creating absolute links and thus pointing to wrong files when mounted (
or appear as broken). For example RPI4 running Debian Buster the library /usr/lib/x86_x64-gnu-linux/libpthread.so is symlink to /lib/x86_x64-gnu-linux/libpthread.so instead
of ../../../lib/x86_x64-gnu-linux/libpthread.so.
Any contribution helping to make this project is welcome
276 followers · starred Apr 2024
841 followers · starred Jul 2022
701 followers · starred Jan 2023
860 followers · starred Feb 2026