manylinux rust/pyo3 build guide
on this page
building python wheels with rust extensions using pyo3 and maturin requires careful management of glibc compatibility to ensure wheels work across different linux distributions.
overview
key challenges when building rust python extensions:
- rust 1.64+ requires glibc 2.17 minimum (manylinux2014)
- modern ubuntu/debian systems have incompatible glibc versions
- pypi only accepts manylinux-tagged wheels, not generic linux
- native builds on ubuntu 25.04 (glibc 2.41) fail manylinux compliance
manylinux standards
manylinux defines maximum glibc versions and allowed external libraries for python wheel compatibility.
glibc version requirements
standard | base os | glibc version | release year | rust 1.64+ compatible | status |
---|---|---|---|---|---|
manylinux1 | centos 5 | 2.5 | 2016 | ❌ | obsolete |
manylinux2010 | centos 6 | 2.12 | 2018 | ❌ | eol |
manylinux2014 | centos 7 | 2.17 | 2019 | ✅ | eol (june 2024) |
manylinux_2_24 | debian 9 | 2.24 | 2020 | ✅ | active |
manylinux_2_28 | almalinux 8 | 2.28 | 2021 | ✅ | active |
manylinux_2_31 | almalinux 9 | 2.31 | 2022 | ✅ | active |
manylinux_2_34 | almalinux 9 | 2.34 | 2023 | ✅ | active |
critical: rust 1.64+ (august 2022) requires glibc 2.17 minimum, making manylinux2014 the oldest supported standard.
platform compatibility
native build failures
modern linux distributions ship with glibc versions incompatible with manylinux requirements:
distribution | glibc version | native build result |
---|---|---|
ubuntu 20.04 lts | 2.31 | ❌ fails compliance |
ubuntu 22.04 lts | 2.35 | ❌ fails compliance |
ubuntu 24.04 lts | 2.39 | ❌ fails compliance |
ubuntu 25.04 (plucky puffin) | 2.41 | ❌ fails compliance |
debian 11 bullseye | 2.31 | ❌ fails compliance |
debian 12 bookworm | 2.36 | ❌ fails compliance |
debian 13 trixie | 2.41 | ❌ fails compliance |
rhel/almalinux 8 | 2.28 | ⚠️ manylinux_2_28 only |
rhel/almalinux 9 | 2.34 | ⚠️ manylinux_2_34 only |
example error on ubuntu 25.04
$ maturin build --release
💥 maturin failed
Caused by: Error ensuring manylinux_2_17 compliance
Caused by: Your library is not manylinux_2_17 (aka manylinux2014) compliant
because of the presence of too-recent versioned symbols:
["libc.so.6 offending versions: GLIBC_2.18, GLIBC_2.25, GLIBC_2.28, GLIBC_2.29, GLIBC_2.33, GLIBC_2.34",
"libm.so.6 offending versions: GLIBC_2.29"]
build strategies
docker container build (recommended)
the official pyo3/maturin docker image provides a controlled build environment with correct glibc versions.
# build manylinux-compliant wheels using docker
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
build --release \
--strip \
--manylinux 2_28 \
-o dist
# for specific python versions
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
build --release \
--strip \
--manylinux 2_28 \
--interpreter python3.11 python3.12 \
-o dist
advantages:
- guaranteed manylinux compliance
- consistent build environment across systems
- no local setup required
disadvantages:
- requires docker installation
- slower than native builds
- may require additional configuration for cross-compilation
zig cross-compilation
use zig as a cross-compiler to target different glibc versions from any host:
# install zig support
pip install maturin[zig]
# build with zig targeting manylinux2014
maturin build --release --zig --manylinux 2_17
# target specific glibc version
maturin build --release --zig --manylinux 2_28
advantages:
- build on any host os including macos
- target arbitrary glibc versions
- no docker required
disadvantages:
- experimental feature
- may have edge cases with complex dependencies
- requires zig installation
github actions ci/cd
automate builds using pyo3/maturin-action:
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v4
- uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto # automatically selects appropriate version
command: build
args: --release --strip
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-linux-${{ matrix.target }}
path: dist
common errors and solutions
glibc version errors
error:
ImportError: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.18' not found
cause: wheel built with newer glibc than target system has
solutions:
# option 1: build in docker
docker run --rm -v $(pwd):/io ghcr.io/pyo3/maturin:latest build --release
# option 2: skip auditwheel (not for pypi)
maturin build --release --auditwheel skip
# option 3: use zig
maturin build --release --zig --manylinux 2_17
auditwheel compliance failures
error:
Your library is not manylinux_2_17 compliant because of the presence of
too-recent versioned symbols
cause: native build links against system glibc
solution: always build in controlled environment (docker or zig)
platform tag issues
error:
ERROR: package-0.1.0-cp311-cp311-linux_x86_64.whl is not a supported wheel on this platform.
cause: generic linux
tag not accepted by pypi
solutions:
- ensure manylinux compliance via docker/zig
- use
--compatibility pypi
flag - verify with
auditwheel show dist/*.whl
python linking errors
error:
undefined reference to `_Py_Dealloc'
undefined reference to `_Py_NoneStruct'
cause: missing python development headers or incorrect cargo configuration
solution:
# Cargo.toml
[dependencies.pyo3]
version = "0.24"
features = ["extension-module", "abi3-py311"] # abi3 for forward compatibility
[lib]
name = "your_module"
crate-type = ["cdylib"]
pypi publishing
authentication setup
option 1: environment variable (recommended)
export MATURIN_PYPI_TOKEN="pypi-YOUR_TOKEN_HERE"
maturin publish
option 2: .pypirc configuration
# ~/.pypirc
[pypi]
username = __token__
password = pypi-YOUR_TOKEN_HERE
important: username must be __token__
(lowercase), not __Token__
publishing workflow
#!/bin/bash
# complete publishing workflow
# 1. build wheels in docker
docker run --rm -v "$(pwd)":/io -w /io \
ghcr.io/pyo3/maturin:latest \
build --release --strip --manylinux 2_28 -o dist
# 2. test locally
pip install dist/*.whl
python -c "import your_module; print(your_module.__version__)"
# 3. upload to testpypi first
export MATURIN_PYPI_TOKEN="pypi-test-YOUR_TEST_TOKEN"
maturin upload --repository testpypi dist/*.whl
# 4. test from testpypi
pip install -i https://test.pypi.org/simple/ your-package
# 5. upload to production pypi
export MATURIN_PYPI_TOKEN="pypi-YOUR_PRODUCTION_TOKEN"
maturin upload dist/*.whl
common publishing errors
403 forbidden:
- verify token has correct scope
- ensure project name matches pypi registration
- check token hasn’t expired
invalid authentication:
- use
__token__
as username (not__Token__
) - ensure no control characters in token
- verify token starts with
pypi-
debugging commands
verify glibc requirements
# check system glibc version
ldd --version
# inspect wheel metadata
unzip -l dist/*.whl | grep WHEEL
python -m zipfile -l dist/*.whl
# check wheel compliance
auditwheel show dist/*.whl
# verify glibc symbol versions (critical for debugging)
objdump -T target/release/lib*.so | grep GLIBC | sort -u
# example output showing problematic symbols:
# 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.29) log
# 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.34) pthread_key_create
# these symbols would prevent running on systems with glibc < 2.34
test wheel installation
# create clean test environment
python -m venv test_env
source test_env/bin/activate
# install and test
pip install dist/*.whl
python -c "import your_module"
# check linked libraries
ldd test_env/lib/python*/site-packages/your_module*.so
platform-specific notes
macos
no manylinux concerns, but ensure universal2 wheels for intel and apple silicon:
# build universal2 wheel
maturin build --release --universal2
# or separate builds
maturin build --release --target x86_64-apple-darwin
maturin build --release --target aarch64-apple-darwin
windows
use github actions or appropriate visual studio toolchain:
- uses: PyO3/maturin-action@v1
with:
target: x86_64-pc-windows-msvc
command: build
args: --release
arm architecture
cross-compilation required for aarch64:
# using docker
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
build --release \
--target aarch64-unknown-linux-gnu \
--manylinux 2_28 \
-o dist
# using zig
maturin build --release --zig \
--target aarch64-unknown-linux-gnu \
--manylinux 2_28
troubleshooting decision tree
build fails with glibc error?
├── yes → build in manylinux docker container
│ ├── still fails? → check rust version >= 1.64
│ └── works → use this approach for ci/cd
└── no → different issue
├── linking errors? → check pyo3 features in cargo.toml
├── upload fails? → check pypi token configuration
└── import fails? → verify wheel platform compatibility
best practices checklist
- always build in manylinux docker container for releases
- use
--strip
flag to reduce wheel size - test on oldest supported python version (e.g., 3.11)
- enable abi3 for forward compatibility
- use
--compatibility pypi
for compliance check - test wheels before publishing
- document minimum glibc requirements in readme
- use github actions for automated builds
- keep build scripts in repository
- version tag releases appropriately
build script example
create build-wheels.sh
in your repository:
#!/bin/bash
set -e
# default to manylinux_2_28 for broad compatibility
MANYLINUX_VERSION=${1:-2_28}
echo "building manylinux${MANYLINUX_VERSION} wheels..."
# clean previous builds
rm -rf dist/
# build wheels in docker
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
build --release \
--strip \
--manylinux ${MANYLINUX_VERSION} \
-o dist
# verify wheel compliance
for wheel in dist/*.whl; do
echo "checking: $wheel"
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
auditwheel show "$wheel"
done
echo "wheels built successfully in dist/"
ls -la dist/
references
official documentation
docker images
- ghcr.io/pyo3/maturin - official maturin image
- quay.io/pypa/manylinux2014 - base manylinux2014
- rust-cross/manylinux-cross - cross-compilation images
github actions
- pyo3/maturin-action - official github action
- actions-rust-lang/setup-rust-toolchain - rust toolchain setup
community resources
last updated: august 11, 2025 based on: rust 1.64+, maturin 1.8.3, pyo3 0.24.0 tested on: ubuntu 25.04 (glibc 2.41), debian 13 trixie (glibc 2.41)