Choosing Between manylinux and musllinux Wheels for PyPI Packages
Learn how manylinux and musllinux wheels let you skip compilation, ensure binary compatibility across Linux distros, and what trade-offs to consider when publishing PyPI packages.
06 Jul 2026, 01:06 UTC

The Compilation Stall
When you run pip install on a package that contains C extensions, the tool first looks for a pre-compiled wheel that matches your platform and Python version. If none is found, pip falls back to a source distribution (sdist) and starts a local build. On a clean CI runner or a minimal laptop this can mean minutes of gcc output, missing header errors, or a failure because the host lacks a compatible glibc version.
Why manylinux Exists
Linux distributions ship different releases of the GNU C Library (glibc). A binary linked against a newer glibc symbol will refuse to start on an older system with the error 'version 'GLIBC_2.XX' not found'. The manylinux standard solves this by defining a very old, widely-present glibc version as the baseline. Any wheel built inside a manylinux-compatible environment is guaranteed to run on any distribution that ships a glibc newer than that baseline—covering Ubuntu, Debian, Fedora, CentOS and many others.
When Alpine Needs musllinux
Alpine Linux uses the musl C library instead of glibc. Because musl and glibc are binary incompatible, a manylinux wheel will not run on Alpine. The musllinux counterpart provides the same baseline idea for musl, allowing maintainers to publish a separate wheel tagged musllinux_1_1 (or newer) that works on Alpine-based images without pulling in build-base or python3-dev.
Worked Example: Making a Portable Wheel with auditwheel
Assume you have a source checkout of myext in the current directory and you want to produce a manylinux2014 wheel for CPython 3.11 on x86_64.
- Start a manylinux Docker image that provides the baseline toolchain.
- Inside the container, build a raw wheel (this will be tagged
linux_x86_64and not yet portable). - Run
auditwheel repairto bundle any external shared libraries and rewrite the RPATH. - The repaired file appears as
myext-1.0.0-cp311-cp311-manylinux2014_x86_64.whl. Upload this to PyPI; pip will select it on any compatible glibc system.
docker run --rm -v "$(pwd)":/io quay.io/pypa/manylinux2014_x86_64 bash
pip wheel . -w /io/wheelhouse
auditwheel repair /io/wheelhouse/myext-1.0.0-cp311-cp311-linux_x86_64.whl -w /io/wheelhouse/manylinux
Where to run: steps 1‑3 inside the container; you need write access to the /io/wheelhouse directory (the default when you mount the current folder). Risks: if your extension links to a large library such as MKL, the repaired wheel can grow to hundreds of megabytes, increasing download time and storage cost.
Trade‑offs and Limitations
- Package size: bundling shared libraries inflates the .whl file. For heavy dependencies the wheel may exceed 100 MB, which can be undesirable for bandwidth‑constrained environments.
- CI overhead: you must maintain separate builds for each manylinux version (e.g., 2010, 2014, 2020) and each architecture (x86_64, aarch64, ppc64le) plus a musllinux build for Alpine.
- Security maintenance: the bundled libraries are static copies; when a CVE is patched in the host distro, you must rebuild and republish the wheel to propagate the fix.
Checking That a Wheel Is Being Used
On the target machine, install with verbose output and watch the filename:
pip install myext --verbose
If you see a line like Collecting myext followed by Downloading myext-1.0.0-cp311-cp311-manylinux2014_x86_64.whl (2.3 MB), the binary wheel is being used. If instead you see Collecting myext then Downloading myext-1.0.0.tar.gz (120 kB) and subsequent Running setup.py install or Building wheel steps, no compatible wheel exists for your platform and glibc version.
You can also inspect the Files tab of the project on PyPI; look for filenames containing manylinux or musllinux tags.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.