Publishing to PyPI
Publishing uploads your built wheel and sdist to the Python Package Index. TestPyPI provides a sandbox; trusted publishing uses OIDC tokens instead of passwords.
Search across all documentation pages
Publishing uploads your built wheel and sdist to the Python Package Index. TestPyPI provides a sandbox; trusted publishing uses OIDC tokens instead of passwords.
uv build
uv publish # uses PyPI token from envexport UV_PUBLISH_TOKEN=pypi-AgEIcH...
uv publish --index pypiWhen to reach for this:
# 1. Build
uv build
ls dist/
# 2. Test on TestPyPI
uv publish --index testpypi
uv pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ myapp
# 3. Publish to production PyPI
uv publish# GitHub Actions trusted publishing (no token in secrets)
permissions:
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build
- uses: pypa/gh-action-pypi-publish@release/v1What this demonstrates:
uv publish wraps twine with project-aware defaultsuv build succeedspip install dist/*.whl works in clean venv| Method | Security | Setup |
|---|---|---|
| Trusted publishing | Best | PyPI project settings + GitHub OIDC |
| API token | Good | pypi- prefixed token in env var |
| Password | Deprecated | Do not use |
readme = "README.md" and license in [project].| Alternative | Use When | Don't Use When |
|---|---|---|
| Private index | Internal packages | Public open-source |
| Git URL install | Unpublished internal | Reusable library |
| Docker image | Application deploy | Library distribution |
uv publish for uv projects. twine works universally: twine upload dist/*.
Register at pypi.org. Enable 2FA. Create project for trusted publishing.
test.pypi.org - sandbox registry. Separate account and tokens.
Yank (hide) yes. Permanent delete only within 24 hours and rarely.
First publish wins. Check availability before branding.
Trusted publishing (recommended) or UV_PUBLISH_TOKEN in secrets.
Everything in dist/ - typically one .whl and one .tar.gz.
readme = "README.md" in pyproject.toml renders on the PyPI page.
classifiers = ["Programming Language :: Python :: 3.14"] in [project].
On meaningful changes. Libraries: semver releases. Apps: as needed.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026