CLI tool
pypa/auditwheel avatar
pypa/auditwheel

auditwheel: relabelling a Linux wheel so it runs on the distribution you meant

Auditing and relabeling cross-distribution Linux wheels.

535 stars169 forksPythonNOASSERTION

At a glance

What is it?
auditwheel is the tool that turns a wheel built on your machine into one that claims to be portable, by finding the shared libraries it still depends on and either reporting them or bundling them into the wheel with the right RPATH. Its two documented limitations are the ones that matter when your build is not quite right.
Who is it for?
Adopt auditwheel if you publish binary wheels for Linux, because relabelling a wheel as manylinux without the work it does produces a package that breaks on some distributions and is hard to debug. Do not rely on it to fix a badly built wheel, because it cannot fix binaries linked against a too-recent libc, and it misses dependencies loaded dynamically through ctypes, cffi or dlopen.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the two subcommands actually do

auditwheel is a command line tool to facilitate the creation of Python wheel packages, containing pre-compiled binary extensions, that are compatible with a wide variety of distributions, consistent with the manylinux platform tags defined across PEP 600, PEP 513, PEP 571, PEP 599 and PEP 738 for Android.

The two subcommands split the work cleanly.

auditwheel show shows external shared libraries that the wheel depends on, beyond the libraries included in the manylinux policies, and checks the extension modules for the use of versioned symbols that exceed the manylinux ABI.

auditwheel repair copies these external shared libraries into the wheel itself, and automatically modifies the appropriate RPATH entries such that these libraries will be picked up at runtime. The README notes this accomplishes a similar result as if the libraries had been statically linked without requiring changes to the build system, and it advises that bundling, like static linking, may implicate copyright concerns.

That last point is the one a packager should not skip. Bundling a copy of libffi into your wheel means shipping someone else's code, and the manylinux policy exists partly to avoid exactly that. The trade is that a manylinux wheel is portable and the copyright question is settled by the policy, while a repaired wheel carries a licence obligation with it.

The project is MIT licensed per the manifest, and the repository records a custom licence GitHub cannot classify with the file in the root. The primary language is Python, the default branch is main, and the last push was on 2026-10-10, with releases 6.8.2 from 2026-08-30, 6.8.1 from 2026-08-15 and 6.8.0 from 2026-08-10, so the cadence is roughly every two weeks.

Running show tells you what your build got wrong

The README's example output is the most useful documentation in the project, because it shows what a failure looks like.

Inspecting a wheel is a single command:

code
    $ auditwheel show cffi-1.5.0-cp35-cp35m-linux_x86_64.whl

    cffi-1.5.0-cp35-cp35m-linux_x86_64.whl is consistent with the
    following platform tag: "linux_x86_64".

    The wheel references the following external versioned symbols in
    system-provided shared libraries: GLIBC_2.3.

    The following external shared libraries are required by the wheel:
    {
        "libc.so.6": "/lib64/libc-2.5.so",
        "libffi.so.5": "/usr/lib64/libffi.so.5.0.6",
        "libpthread.so.0": "/lib64/libpthread-2.5.so"
    }

    In order to achieve the tag platform tag "manylinux1_x86_64" the
    following shared library dependencies will need to be eliminated:

    libffi.so.5

Two things are happening at once. The platform tag check tells you the wheel currently claims to be a plain linux_x86_64 wheel, which promises nothing about which distribution it runs on. The library list then tells you what it actually needs, and the final section names the ones that would have to go for the wheel to qualify as manylinux1.

Note that the versioned symbol check is separate from the library check. The wheel references GLIBC_2.3, which is old enough to be fine, and the offending symbol would be one exceeding the manylinux ABI. A wheel can link only permitted libraries and still fail on a newer glibc, and the two checks catch different mistakes.

Repair grafts the libraries in and rewrites the tags

The repair output is worth reading line by line, because each line is a step in the transformation:

code
    $ auditwheel repair cffi-1.5.2-cp35-cp35m-linux_x86_64.whl
    Repairing cffi-1.5.2-cp35-cp35m-linux_x86_64.whl
    Grafting: /usr/lib64/libffi.so.5.0.6
    Setting RPATH: _cffi_backend.cpython-35m-x86_64-linux-gnu.so to "$ORIGIN/.libs_cffi_backend"
    Previous filename tags: linux_x86_64
    New filename tags: manylinux1_x86_64
    Previous WHEEL info tags: cp35-cp35m-linux_x86_64
    New WHEEL info tags: cp35-cp35m-manylinux1_x86_64

    Fixed-up wheel written to /wheelhouse/cffi-1.5.2-cp35-cp35m-manylinux1_x86_64.whl

Grafting is the copy of libffi into the wheel. Setting RPATH to $ORIGIN/.libs_cffi_backend is the part that makes it work at runtime, because $ORIGIN resolves to the directory of the shared object itself, which is inside the unpacked wheel, so the loader finds the bundled copy before the system one.

Then there are two tag changes, and they are not the same thing. The filename tags change from linux_x86_64 to manylinux1_x86_64, and the WHEEL info tags change separately, because the filename tag and the metadata tag are two different fields in the wheel format and both have to agree.

The output ends with the fixed-up wheel written to the wheelhouse, which is the conventional output directory for the wheels you are about to upload.

The build has to happen on an old distribution

The Requirements section and the Limitations section together describe the workflow that actually works, and it is not a plain one.

The requirements are Linux, or macOS for Android wheels, Python 3.10 or newer, and patchelf at 0.14.5 or newer. Only systems that use ELF-based linkage are supported, which the README says should be essentially every Linux.

Then the detail that decides where the build happens. Building manylinux1 wheels requires running on a CentOS5 machine, building manylinux2010 wheels requires running on a CentOS6 machine, and building manylinux2014 wheels requires running on a CentOS7 machine, so the README recommends using the pre-built manylinux Docker images, for example:

code
  $ docker run -i -t -v `pwd`:/io quay.io/pypa/manylinux_2_28_x86_64 /bin/bash

So the modern answer is the manylinux_2_28 image rather than a literal CentOS, but the principle is the same and it is the second limitation that explains it.

The second limitation says there is nothing we can do about fixing binaries if they were compiled and linked against a too-recent version of libc or libstdc++. These libraries use symbol versioning for backward compatibility, so in general code compiled against an old glibc runs fine on systems with a newer glibc, but code compiled on a new system will not, or might not, run on an older system. The conclusion the README draws is that to compile widely-compatible binaries you are best off doing the build on an old Linux distribution, such as a manylinux Docker image.

That is why auditwheel is a labelling and bundling tool rather than a fixing tool. It cannot undo a link against a symbol version that did not exist on the target system, so the build environment has to be right before the wheel is ever handed to auditwheel.

What auditwheel cannot see

The first limitation is the one that surprises people, and it is a limitation of the analysis rather than of the tool.

auditwheel uses the DT_NEEDED information, the same kind of data ldd reports, from the Python extension modules to determine which system libraries they depend on. Code that dynamically loads libraries at runtime using ctypes or cffi from Python, or dlopen from C or C++, does not contain that information in a way that can be statically determined, so dependencies loaded through those mechanisms will be missed.

So a wheel that loads a library at runtime through ctypes will pass the audit, and then fail on a machine without that library, with a message that points at the Python side rather than at the packaging. That failure mode is worth recognising when it appears, because the natural reaction is to suspect the audit and the wrong reaction is to suspect the audit.

The fix is on the build side rather than in auditwheel. If your extension loads a library dynamically, the library has to be bundled or the dependency has to be declared some other way, and auditwheel will not do it for you.

The second limitation, about too-recent libc, is the one that makes the manylinux container not optional. The two limitations together are the whole argument for building in the container: auditwheel can check the first kind of problem but not the second, and it cannot repair the second kind at all.

Running the tests means pulling a set of Docker images

The Testing section is short, and it explains the test setup more than most projects bother to.

The tests can be run with nox, which will automatically install test dependencies. Some of the integration tests also require a running and accessible Docker daemon, and those tests will pull a number of Docker images if they are not already available on your system, but they will not update existing images. To update them manually you run:

code
    docker pull python:3.10-slim-trixie
    docker pull quay.io/pypa/manylinux2010_x86_64
    docker pull quay.io/pypa/manylinux2014_x86_64
    docker pull quay.io/pypa/manylinux_2_28_x86_64
    docker pull quay.io/pypa/manylinux_2_34_x86_64
    docker pull quay.io/pypa/musllinux_1_2_x86_64

And you may also remove these images using docker rmi.

The list of images is the interesting part, because it is the compatibility matrix in image form. There are the two manylinux2010 and manylinux2014 images, the two newer manylinux_2_28 and manylinux_2_34 images, a musllinux_1_2 image for the musl based distributions, and a plain python slim image. A project that supports all of those is claiming compatibility with both glibc and musl Linux, and with the older manylinux baselines as well as the current ones.

The repository layout matches the tooling. There is a noxfile.py for the test sessions, a conftest.py, a src/ directory with the package, a tests/ directory, a scripts/ directory, and a pyproject.toml whose setuptools configuration points the package search at src with namespaces set to false, and whose package data includes the json files that hold the policy data. The runtime dependencies are just packaging and pyelftools, so the ELF parsing is done with a dedicated library rather than by shelling out to a system tool, and the console script is auditwheel mapped to auditwheel.main:main.

Editorial conclusion

Adopt auditwheel if you publish binary wheels for Linux, because relabelling a wheel as manylinux without the work it does produces a package that breaks on some distributions and is hard to debug. Do not rely on it to fix a badly built wheel, because it cannot fix binaries linked against a too-recent libc, and it misses dependencies loaded dynamically through ctypes, cffi or dlopen. Before you run it, build the wheel inside a manylinux container rather than on your own distribution, and check the show output first, since the dependencies it reports are the ones your build system got wrong.

Frequently asked questions

What does auditwheel show and repair do?

show reports the external shared libraries the wheel depends on, beyond the libraries the manylinux policies allow, and checks the extension modules for versioned symbols exceeding the manylinux ABI. repair copies those external shared libraries into the wheel and modifies the RPATH entries so they are picked up at runtime, then relabels the wheel with the appropriate manylinux tag.

Why does my repaired wheel still fail on older Linux systems?

auditwheel cannot fix binaries that were compiled and linked against a too-recent version of libc or libstdc++. Code compiled on a new system may not run on an older one, so to compile widely-compatible binaries you should build on an old Linux distribution, for example a manylinux Docker image.

Does auditwheel detect libraries loaded with ctypes or dlopen?

No. It uses the DT_NEEDED information, the same kind of data ldd reports, so dependencies loaded dynamically through ctypes, cffi or dlopen do not appear in that information and are missed by the audit.

What does auditwheel need installed?

Linux, or macOS for Android wheels, Python 3.10 or newer, and patchelf at 0.14.5 or newer. It is installed with pip, and the console script is auditwheel. Only systems using ELF-based linkage are supported.

Official sources

  1. Issues
  2. pypa/auditwheel on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pypa-auditwheel.svg)](https://hysenlabs.com/projects/pypa-auditwheel)