uv publish: Upload Distributions to an Index
uv publish uploads the distributions in dist/ to PyPI or a configured package index.
uv publish replaces twine upload in a uv workflow. It pushes the artifacts from uv build to an index, with token or trusted-publishing auth.
What it does
uv publish uploads the wheels and sdists it finds (dist/* by default) to the target index. Credentials come from --token, the UV_PUBLISH_TOKEN env var, a username/password, or GitHub Actions trusted publishing when no token is given.
Common usage
uv build
uv publish
uv publish --token "$PYPI_TOKEN"
uv publish --publish-url https://test.pypi.org/legacy/
uv publish dist/*Options
| Flag | What it does |
|---|---|
| --token <token> | API token (or set UV_PUBLISH_TOKEN) |
| --publish-url <url> | Upload endpoint for a non-PyPI index |
| --username / --password | Basic credentials instead of a token |
| --index <name> | Publish to an index defined in pyproject.toml |
| --check-url <url> | Skip files already present at this index URL |
In CI
Prefer GitHub Actions trusted publishing: run uv publish with no token and let OIDC authenticate. Otherwise store the token as a secret and pass it via UV_PUBLISH_TOKEN. Use --check-url so a re-run skips already-uploaded files instead of failing.
Common errors in CI
"error: Failed to publish: 403 Forbidden" means a bad or scopeless token, or the trusted-publishing trust relationship is not configured. "400 Bad Request: File already exists" (or 409) means that exact version was already uploaded; bump the version or pass --check-url. "Permission denied (publish is not allowed)" can mean the project name is taken.