manylinux
- title
- manylinux
- type
- toolbox
- summary
- PEP-defined platform tags and old-glibc Docker images for portable Python binary wheels
- tags
- python, packaging, abi, linux
- language
- Shell
- license
- MIT
- created
- 2026-07-21
- updated
- 2026-07-21
manylinux is a convention plus a set of prebuilt Docker images for producing binary Python extension wheels that install and run across most Linux distributions. A wheel with C or Rust extensions is compiled against a specific glibc and a specific set of system libraries; build it naively on your dev machine and it may refuse to load on someone else's. manylinux exists so that "one wheel, most Linuxes" is achievable, and it's really a series of PEPs defining what "most Linuxes" means, with images that let you build against that baseline.
How it works
The core insight is that a binary built on one distribution runs on distributions the same age or newer, but not older โ a wheel linked against a new glibc won't load where glibc is older. So manylinux builds on deliberately old base images, and the resulting wheel works on everything from that vintage forward. The platform tag on the wheel encodes exactly which baseline it targets, and the installer (pip) checks the tag against the host before installing.
The platform-tag lineage is a chain of PEPs:
- PEP 513 โ manylinux1, based on CentOS 5
- PEP 571 โ manylinux2010, CentOS 6
- PEP 599 โ manylinux2014, CentOS 7
- PEP 600 โ the future-proof
manylinux_x_yscheme, keyed directly to a glibc version (e.g.manylinux_2_28means glibc 2.28) instead of minting a new named tag per distro generation - PEP 656 โ
musllinux_x_y, the same idea keyed to musl for Alpine and other musl systems
Images live at quay.io/pypa/... and are tagged so a build can pin an exact image for repeatable results. Current images include manylinux2014, manylinux_2_28 (AlmaLinux 8), manylinux_2_34 (AlmaLinux 9, alpha), manylinux_2_39 (AlmaLinux/Rocky 10, alpha, and the first to add riscv64), manylinux_2_31 and manylinux_2_35 (armv7l), and musllinux_1_2 (Alpine 3.22). Architectures covered across the set: x86_64, i686, aarch64, ppc64le, s390x, armv7l, riscv64. The RHEL-derivative images use yum/dnf; the armv7l images are Debian-based and use apt instead.
Usage
docker run --rm -v $(pwd):/io quay.io/pypa/manylinux_2_28_x86_64 \
/io/build-wheels.sh
# inside: build with an old-glibc toolchain, then run auditwheel repair
auditwheel repair dist/mypkg-*.whl -w /io/wheelhouse/
auditwheel is the companion tool: it inspects a built wheel, confirms it only depends on libraries in the manylinux baseline, and grafts any extra shared libraries into the wheel so it's self-contained.
Limitations
- The biggest sharp edge: RHEL 9 and its derivatives target the
x86-64-v2micro-architecture. Libraries pulled in viadnfon amanylinux_2_28+ image can bev2-compiled and grafted into a wheel, which then fails on older x86_64 hardware that predatesv2. No PEP handles micro-architecture variants, and auditwheel does not detect this โ so a wheel can pass every check and still crash on an older CPU. - Old-baseline images mean an old toolchain. Building against manylinux2014 (CentOS 7) gives you a compiler and system libraries from that era unless you install newer ones yourself.
- Alpha images (
manylinux_2_34,_2_39) aren't stable targets yet.
This is the wheel-packaging face of ABI stability: manylinux is Python's answer to glibc's habit of breaking binaries, achieved by building old and bundling everything, much like vendoring ships self-contained artifacts rather than trusting the host. Compare win32-stable-abi for the argument that the C library is the unstable layer on Linux, and supply-chain-security for why grafting third-party libraries into a wheel is also a trust-boundary question.
Repo
github.com/pypa/manylinux โ Shell, MIT, ~1.76k stars.