How to test your package across Linux distributions
You can catch most distribution-specific failures with a short loop over clean container images. Here is a simple setup and what it will not catch.
Test locally with Docker
Run the same check in a fresh container for each system. Mount the binary as read-only and let the exit code decide:
for image in ubuntu:24.04 debian:12 fedora:41 almalinux:9 alpine:3.20; do
if docker run --rm -v "$PWD/mytool:/usr/local/bin/mytool:ro" "$image" mytool --version; then
echo "PASS $image"
else
echo "FAIL $image"
fi
done
Run it on every change in CI
name: distro-test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image: ["ubuntu:24.04", "debian:12", "fedora:41", "almalinux:9", "alpine:3.20"]
container: ${{ matrix.image }}
steps:
- uses: actions/download-artifact@v4
with:
name: mytool
- run: chmod +x mytool && ./mytool --version
This assumes an earlier job uploaded the binary as an artifact named mytool and that the job depends on it. Keep fail-fast: false so one failure does not cancel the other systems and hide their results. Some JavaScript-based actions do not run in Alpine containers, so that image may need extra setup.
What to test
- Do a real install. Install the way your users do, with your
.deb,.rpmor install script, not only by copying a binary. - Run real work.
--versionproves the binary starts. It does not exercise a library loaded later. - Cover old and new. Include your oldest supported release, because that is where glibc errors show up.
Where containers fall short
- Shared kernel. Every container uses the host's kernel, so kernel modules and kernel-specific behaviour are not tested.
- Minimal images. Container images often lack systemd and common tools, so they differ from a full server install.
- Architecture. Testing on x86-64 says nothing about ARM. See exec format error.
- Untrusted code. A container is a weak boundary for code you do not trust. Use a virtual machine for that.