Skip to main content

Overview

uv supports building Python packages into source and binary distributions and uploading them to package registries like PyPI.

Preparing your project

Before publishing, ensure your project is ready for distribution.

Configure a build system

If your pyproject.toml lacks a [build-system] definition, uv won’t build it during uv sync, but will fall back to legacy setuptools during uv build.
We strongly recommend configuring a build system explicitly.
Add a build system to your pyproject.toml:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Read more about build systems.

Building your package

1

Build distributions

uv build
By default, this builds both:
  • Source distribution (.tar.gz)
  • Wheel (.whl)
Artifacts are placed in dist/:
ls dist/
# hello-world-0.1.0-py3-none-any.whl
# hello-world-0.1.0.tar.gz
2

Build specific directory

uv build <SRC>
3

Build workspace package

uv build --package <PACKAGE>
When publishing, run uv build --no-sources to ensure the package builds correctly without tool.uv.sources, as other build tools like pypa/build don’t support them.

Updating your version

The uv version command helps manage your package version before publishing.

View current version

uv version
# hello-world 0.7.0
See viewing your version for details.

Set exact version

uv version 1.0.0
# hello-world 0.7.0 => 1.0.0

Preview changes

uv version 2.0.0 --dry-run
# hello-world 1.0.0 => 2.0.0

uv version
# hello-world 1.0.0  (unchanged)

Bump version semantically

Use --bump with version components:
uv version --bump minor
# hello-world 1.2.3 => 1.3.0

Pre-release versions

uv version --bump patch --bump beta
# hello-world 1.3.0 => 1.3.1b1

Set specific component values

uv version --bump patch --bump dev=66463664
# hello-world 0.0.1 => 0.0.2.dev66463664
By default, uv version performs lock and sync. Use --frozen to prevent both, or --no-sync to only prevent syncing.

Publishing your package

1

Get credentials

Set PyPI credentials using environment variables or command options:
export UV_PUBLISH_TOKEN="your-token"
PyPI no longer supports username/password authentication. Generate a token instead, which is equivalent to --username __token__ with the token as password.
2

Publish to PyPI

uv publish
For GitHub Actions or other Trusted Publishers, add a trusted publisher to PyPI — no credentials needed!
A complete guide to publishing from GitHub Actions to PyPI is available in the GitHub Guide.

Publishing to custom indexes

Configure custom indexes in pyproject.toml:
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true
Then publish:
uv publish --index testpypi
When using uv publish --index <name>, pyproject.toml must be present (e.g., include a checkout step in CI).

Handling failed uploads

uv publish retries failed uploads, but if some files upload and others fail:
Retry the same command — existing identical files are ignored:
uv publish
Use --check-url with the index URL:
uv publish --check-url https://registry.example.com/simple/
uv skips uploading files identical to those in the registry and handles parallel upload races.
Existing files must match exactly. This prevents accidentally publishing distributions with different contents for the same version.

Uploading attestations

uv supports uploading attestations to registries like PyPI.
uv publish doesn’t generate attestations — create them separately before publishing.
uv automatically discovers and matches attestations:
ls dist/
# hello_world-1.0.0-py3-none-any.whl
# hello_world-1.0.0-py3-none-any.whl.publish.attestation
# hello_world-1.0.0.tar.gz
# hello_world-1.0.0.tar.gz.publish.attestation

uv publish
# Uploads both distributions and their attestations
Some third-party indexes may not support attestations and might reject uploads that include them. Use --no-attestations or UV_PUBLISH_NO_ATTESTATIONS to disable if needed.

Testing your package

After publishing, test that it installs correctly:
uv run --with <PACKAGE> --no-project -- python -c "import <PACKAGE>"
The --no-project flag avoids using your local project directory.
If you just published, add --refresh-package <PACKAGE> to avoid cached versions.

Complete workflow examples

First-time package release

1

Prepare project

Ensure you have:
  • A configured build system
  • Complete project metadata
  • Tests passing
2

Set version

uv version 1.0.0
3

Build distributions

uv build --no-sources
4

Test build

ls dist/
# Verify both .whl and .tar.gz exist
5

Publish to TestPyPI first

uv publish --index testpypi
6

Test installation

uv run --with my-package --no-project --index https://test.pypi.org/simple/ -- python -c "import my_package"
7

Publish to PyPI

uv publish

Releasing a new version

1

Make your changes

Develop and test new features.
2

Bump version

uv version --bump minor  # or major, patch
3

Update changelog

Document changes in CHANGELOG.md.
4

Build and publish

uv build --no-sources
uv publish
5

Tag release

git tag v$(uv version --short)
git push --tags

Publishing from GitHub Actions

1

Set up PyPI trusted publisher

2

Create workflow

.github/workflows/publish.yml
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for trusted publishing
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v4
      - run: uv build --no-sources
      - run: uv publish
3

Create a release

GitHub Actions automatically builds and publishes.

Best practices

Test on TestPyPI first:
uv publish --index testpypi
This catches issues without affecting your PyPI release.
Follow SemVer:
  • Major: Breaking changes
  • Minor: New features, backward compatible
  • Patch: Bug fixes
uv version --bump minor  # 1.2.3 => 1.3.0
Check what’s included in your distributions:
tar -tzf dist/hello-world-0.1.0.tar.gz
unzip -l dist/hello_world-0.1.0-py3-none-any.whl
Use .gitignore and MANIFEST.in to control contents.
Use GitHub Actions or similar for:
  • Automated testing
  • Building on multiple platforms
  • Publishing on release
See the GitHub integration guide.
Maintain a CHANGELOG.md and create GitHub releases with notes.

Next steps

PyPA build guide

Official Python packaging guides

Integration guides

Integrate uv with other tools

GitHub integration

CI/CD with GitHub Actions

Build systems

Configure build systems

Build docs developers (and LLMs) love