GitHub Action to install and run a custom maturin command with built-in support for cross compilation.
- uses: PyO3/maturin-action@v1
with:
command: build
args: --releaseTo generate a GitHub Actions workflow for your project, try the maturin generate-ci github command.
mkdir -p .github/workflows
maturin generate-ci github > .github/workflows/CI.ymlIf you want to build and publish a Python extension module for common Python versions, operating systems, and CPU architectures, take a look at the following examples:
- messense/crfs-rs: PyO3 abi3 wheel example
- messense/rjmespath-rs: PyO3 abi3 wheel with Rust nightly toolchain example
- astral-sh/uv: Hardened binary publishing example
- milesgranger/pyrus-cramjam: PyO3 non-abi3 wheel example
- messense/auditwheel-symbols:
binbinding example using MUSL libc - adriangb/graphlib2: PyO3 abi3 wheel
- pydantic/pydantic-core: PyO3 non-abi3 wheel with PyPy support example
- messense/py-dissimilar: PyO3 non-abi3 wheel with PyPy support example
| Name | Required | Description | Type | Default |
|---|---|---|---|---|
| command | No | maturin command to run |
string | build |
| args | No | Arguments to pass to maturin subcommand |
string | |
| maturin-version | No | The version of maturin to use. Must match a tagged release |
string | latest |
| manylinux | No | Control the manylinux platform tag on linux, ignored on other platforms, use auto to build for lowest compatible |
string | Defaults to auto for the publish command |
| target | No | The --target option for Cargo |
string | |
| container | No | manylinux docker container image name | string | Default depends on target and manylinux options, Set to off to disable manylinux docker build and build on the host instead. |
| docker-options | No | Additional Docker run options, for passing environment variables and etc. | string | |
| rust-toolchain | No | Rust toolchain name. | string | Defaults to stable for Docker build. To use the latest available version for the host build, the user must specify this in the CI config or repo config. |
| rustup-components | No | Rustup components | string | Defaults to empty |
| working-directory | No | The working directory to run the command in | string | Defaults to the root of the repository |
| sccache | No | Enable sccache for faster builds | boolean | Defaults to false |
| before-script-linux | No | Script to run before the maturin command on Linux | string |
By default, this action uses the following containers for supported architectures and manylinux versions.
| Architecture | Manylinux version | Default container | Note |
|---|---|---|---|
| x86_64 | 2010/2_12 | quay.io/pypa/manylinux2010_x86_64:latest | |
| x86_64 | 2014/2_17 | quay.io/pypa/manylinux2014_x86_64:latest | |
| x86_64 | 2_24 | quay.io/pypa/manylinux_2_24_x86_64:latest | Deprecated |
| x86_64 | 2_28 | quay.io/pypa/manylinux_2_28_x86_64:latest | |
| i686 | 2010/2_12 | quay.io/pypa/manylinux2010_i686:latest | |
| i686 | 2014/2_17 | quay.io/pypa/manylinux2014_i686:latest | |
| i686 | 2_24 | quay.io/pypa/manylinux_2_24_i686:latest | Deprecated |
| i686 | 2_28 | quay.io/pypa/manylinux_2_28_i686:latest | |
| aarch64 | 2014/2_17 | ghcr.io/rust-cross/manylinux2014-cross:aarch64 | |
| aarch64 | 2_24 | messense/manylinux_2_24-cross:aarch64 | Deprecated |
| aarch64 | 2_28 | ghcr.io/rust-cross/manylinux_2_28-cross:aarch64 | |
| armv7l | 2014/2_17 | ghcr.io/rust-cross/manylinux2014-cross:armv7 | |
| armv7l | 2_24 | messense/manylinux_2_24-cross:armv7 | Deprecated |
| armv7l | 2_28 | ghcr.io/rust-cross/manylinux_2_28-cross:armv7 | |
| ppc64le | 2014/2_17 | ghcr.io/rust-cross/manylinux2014-cross:ppc64le | |
| ppc64le | 2_24 | messense/manylinux_2_24-cross:ppc64le | Deprecated |
| ppc64le | 2_28 | ghcr.io/rust-cross/manylinux_2_28-cross:ppc64le | |
| ppc64 | 2014/2_17 | ghcr.io/rust-cross/manylinux2014-cross:ppc64 | |
| s390x | 2014/2_17 | ghcr.io/rust-cross/manylinux2014-cross:s390x | |
| s390x | 2_24 | messense/manylinux_2_24-cross:s390x | Deprecated |
| s390x | 2_28 | ghcr.io/rust-cross/manylinux_2_28-cross:s390x | |
| riscv64 | 2_31 | ghcr.io/rust-cross/manylinux_2_31-cross:riscv64 | |
| riscv64 | 2_39 | quay.io/pypa/manylinux_2_39_riscv64:latest | |
| loongarch64 | 2_36 | ghcr.io/rust-cross/manylinux_2_36-cross:loongarch64 |
You can override it by supplying the container input.
Note that if use official manylinux docker images for platforms other than x86_64 and i686,
you will need to setup QEMU before using this action, for example
- name: Setup QEMU
uses: docker/setup-qemu-action@v1
- uses: PyO3/maturin-action@v1
with:
command: build
args: --releaseNote that the actions/setup-python action won't affect manylinux build since it's containerized,
so if you want to build for certain Python version for Linux, use -i pythonX.Y in the args option in
PyO3/maturin-action instead, for example
- uses: PyO3/maturin-action@v1
with:
args: --release -i python3.10To build for every available interpreter at once — including the free-threaded builds — use
--find-interpreter; see Free-threaded CPython below.
maturin builds wheels for the free-threaded ("no-GIL") CPython
builds automatically when you pass --find-interpreter and a free-threaded interpreter is
available. Free-threaded interpreters carry a t suffix (python3.14t, python3.15t, …); maturin
discovers the officially supported ones (CPython 3.14 and newer) the same way it discovers the
regular builds — the experimental 3.13t is not discovered automatically. Discovery needs a
reasonably recent maturin, which the action installs by default.
No configuration needed — the default manylinux containers ship the free-threaded interpreters, and
the action puts every interpreter under /opt/python on PATH, so --find-interpreter finds them:
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --find-interpreterThese run on the host, so the interpreters come from your own actions/setup-python step. Install
the free-threaded build alongside the regular one:
- uses: actions/setup-python@v6
with:
python-version: |
3.14
3.14t
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --find-interpretersetup-python exposes the free-threaded build under its t-suffixed name (python3.14t, or
python3.14t.exe on Windows), which is what --find-interpreter looks for.
On Windows, co-installing the regular and free-threaded interpreters of the same minor version in
one setup-python step can fail
(python/cpython#127294,
#313). Use a matrix with one interpreter per
job instead.
The free-threaded build has its own stable ABI, abi3t (PEP 803, added in CPython 3.15), distinct from the GIL-enabled abi3. PyO3 exposes both as Cargo features, and projects can enable both when they want stable ABI wheels by default:
pyo3 = { version = "0.29", features = ["abi3-py310", "abi3t-py315"] }One maturin invocation selects at most one stable ABI family, so do not expect one
--find-interpreter build to emit both forward-compatible wheels. To publish a complete wheel set
for current non-EOL CPython releases, run separate maturin builds with different interpreters. The
same default Cargo features can be used for each build:
- name: Build abi3 wheel
uses: PyO3/maturin-action@v1
with:
args: --release -i python3.10
- name: Build CPython 3.14t wheel
uses: PyO3/maturin-action@v1
with:
args: --release -i python3.14t
- name: Build abi3t wheel
uses: PyO3/maturin-action@v1
with:
args: --release -i python3.15tThe abi3-py310 wheel supports GIL-enabled CPython 3.10 and newer. The abi3t-py315 wheel
supports CPython 3.15 and newer, both GIL-enabled and free-threaded. Free-threaded CPython 3.14
predates abi3t, so the python3.14t build produces the version-specific cp314-cp314t wheel.
If stable ABI support is behind a project feature, pass that feature to both builds.
If a project enables only abi3 (no abi3t), --find-interpreter builds no free-threaded stable
ABI wheel; request a version-specific free-threaded wheel explicitly with, e.g., -i python3.14t.
We recommend the following steps for hardening release pipelines:
- When targeting PyPI, set
--compatibility pypito activate its pre-upload check - Set an explicit
manylinux:version for each target to prevent silent regressions - Pin both maturin-action and maturin version, and use a service such as renovate to update them
strategy:
matrix:
platform:
- target: aarch64-unknown-linux-gnu
arch: aarch64
manylinux: 2_28
- target: armv7-unknown-linux-gnueabihf
arch: armv7
manylinux: 2_17
steps:
# [...]
- name: "Build wheels"
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
with:
maturin-version: v1.11.5
target: ${{ matrix.platform.target }}
manylinux: ${{ matrix.platform.manylinux }}
args: --release --locked --compatibility pypiAn example renovate configuration
// Maturin version used in maturin-action
{
customType: "regex",
managerFilePatterns: ["/.github/workflows/.*\\.yml$/"],
matchStrings: ["maturin-version: (?<currentValue>v\\d+\\.\\d+\\.\\d+)"],
depNameTemplate: "maturin",
packageNameTemplate: "PyO3/maturin",
datasourceTemplate: "github-releases",
},To build after code changes:
npm run allThis work is released under the MIT license. A copy of the license is provided in the LICENSE file.