
Python packaging has been a 15-year embarrassment. setup.py gave way to setup.cfg, which gave way to pyproject.toml, which spawned Poetry, Flit, Hatch, and a thousand Stack Overflow threads about whether MANIFEST.in is still needed (it isn’t, mostly). On June 3, 2026, uv-build 0.11.19 landed on PyPI with development status “5 — Production/Stable.” That’s the last piece of the puzzle. uv can now replace the entire Python packaging stack — and the build backend going stable means you can ship that in production with confidence.
What “Stable” Actually Means
uv itself is still pre-1.0, which has made some developers hesitant to commit. But Astral has explicitly marked uv_build as production-stable and recommends it for real workloads today. The distinction matters: the build backend has a stable interface, predictable behavior, and won’t break your release pipeline without warning. The broader uv tooling may still have pre-1.0 API churn — the build backend specifically does not.
Performance-wise, the numbers are significant. The pydevtools breakdown puts uv_build at 10x–35x faster than flit, hatchling, and setuptools. In CI, that translates to a build step that took 40 seconds dropping to 1–3 seconds. At scale, that adds up. And because uv is written in Rust, you can build and publish a package without Python installed on the machine — a meaningful advantage in minimal container environments.
Two Lines. That’s the Setup
If your project is pure Python (no C extensions, no Cython), switching to uv_build takes two lines in pyproject.toml:
[build-system]
requires = ["uv_build>=0.11,<0.12"]
build-backend = "uv_build"
Then run uv build. It produces both a source distribution (.tar.gz) and a wheel (.whl) in one shot. The build backend auto-discovers your package layout — both the src layout (src/mylib/__init__.py) and flat layout (mylib/__init__.py) work without additional configuration. Package name is normalized from pyproject.toml automatically.
That’s the zero-config promise, and it largely delivers. For the vast majority of pure-Python libraries — the kind of thing you’d previously configure with three different files — this works out of the box.
Migrating Your Existing Project
The migration path depends on where you’re coming from.
From setup.py/setup.cfg: Move your metadata to the [project] table in pyproject.toml. Update the [build-system] block as shown above. Delete setup.py, setup.cfg, and MANIFEST.in. Run uv build to verify.
From Poetry: The migrate-to-uv tool handles most of the conversion automatically. Dependency specs move from [tool.poetry.dependencies] to [project.dependencies], and poetry.lock is replaced by uv.lock. Change the [build-system] block last, after you’ve verified your dependencies resolve correctly.
From setuptools + pip-compile: Change the [build-system] block, then replace your requirements.txt workflow with uv lock and uv sync. The new uv export --emit-index-url flag (added in 0.11.20 on June 10) outputs a compatible requirements.txt if downstream tooling still needs it.
Shipping to PyPI with GitHub Actions
The CI story is clean. uv publish supports OIDC Trusted Publishing, which means no PyPI secrets in your repository. Add id-token: write to your job permissions and it handles authentication via short-lived tokens:
name: Publish
on:
push:
tags: ["v*"]
permissions:
id-token: write
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build
- run: uv publish
That’s the complete publish workflow. No secrets rotation, no token management, no separate Twine step. PyPI’s Trusted Publishing is the right approach here and uv makes it trivial to use.
Where uv_build Stops
Here’s the catch, and it’s a real one: uv_build only supports pure Python. If your project includes C extensions, Cython modules, or Rust bindings, stop here. The alternatives:
- Rust extensions (PyO3): Use maturin. It’s excellent.
- C/C++ extensions: Use scikit-build-core (CMake-based) or setuptools.
- Build hooks or VCS-derived versions: Use Hatchling with hatch-vcs.
- Complex packaging “flavors”: Hatchling’s plugin system handles this; uv_build does not.
The official docs are upfront about this. For pure-Python work — which covers the majority of libraries, CLI tools, and internal packages — uv_build is the right call. For everything else, uv as the project manager still works; you just swap in a different build backend.
The Default Has Changed
Since July 2025, uv init has defaulted to uv_build. With the backend now hitting stable, that default is no longer provisional. If you’re starting a new pure-Python library in 2026, uv_build is the answer. If you’re maintaining an existing project with setup.py still in the repo, this is your sign to schedule the migration.
The Hacker News thread said it best: “This is the last tool I needed to switch. setup.py is finally dead for me.” For pure-Python projects, the packaging landscape is now genuinely clean. That took a while.













