> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/astral-sh/uv/llms.txt
> Use this file to discover all available pages before exploring further.

# Building and Publishing

> Build and publish Python packages with uv build and uv publish

## 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`.

<Warning>
  We strongly recommend configuring a build system explicitly.
</Warning>

Add a build system to your `pyproject.toml`:

<CodeGroup>
  ```toml Hatchling (recommended) theme={null}
  [build-system]
  requires = ["hatchling"]
  build-backend = "hatchling.build"
  ```

  ```toml Setuptools theme={null}
  [build-system]
  requires = ["setuptools>=61.0"]
  build-backend = "setuptools.build_meta"
  ```

  ```toml PDM theme={null}
  [build-system]
  requires = ["pdm-backend"]
  build-backend = "pdm.backend"
  ```

  ```toml Poetry theme={null}
  [build-system]
  requires = ["poetry-core"]
  build-backend = "poetry.core.masonry.api"
  ```
</CodeGroup>

Read more about [build systems](../concepts/projects/config.md#build-systems).

## Building your package

<Steps>
  <Step title="Build distributions">
    ```bash theme={null}
    uv build
    ```

    By default, this builds both:

    * Source distribution (`.tar.gz`)
    * Wheel (`.whl`)

    Artifacts are placed in `dist/`:

    ```bash theme={null}
    ls dist/
    # hello-world-0.1.0-py3-none-any.whl
    # hello-world-0.1.0.tar.gz
    ```
  </Step>

  <Step title="Build specific directory">
    ```bash theme={null}
    uv build <SRC>
    ```
  </Step>

  <Step title="Build workspace package">
    ```bash theme={null}
    uv build --package <PACKAGE>
    ```
  </Step>
</Steps>

<Warning>
  When publishing, run `uv build --no-sources` to ensure the package builds correctly without `tool.uv.sources`, as other build tools like [`pypa/build`](https://github.com/pypa/build) don't support them.
</Warning>

## Updating your version

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

### View current version

<CodeGroup>
  ```bash Full output theme={null}
  uv version
  # hello-world 0.7.0
  ```

  ```bash Short format theme={null}
  uv version --short
  # 0.7.0
  ```

  ```bash JSON format theme={null}
  uv version --output-format json
  # {
  #     "package_name": "hello-world",
  #     "version": "0.7.0",
  #     "commit_info": null
  # }
  ```
</CodeGroup>

See [viewing your version](./projects.mdx#viewing-your-version) for details.

### Set exact version

```bash theme={null}
uv version 1.0.0
# hello-world 0.7.0 => 1.0.0
```

### Preview changes

```bash theme={null}
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:

<CodeGroup>
  ```bash Bump minor theme={null}
  uv version --bump minor
  # hello-world 1.2.3 => 1.3.0
  ```

  ```bash Bump patch theme={null}
  uv version --bump patch
  # hello-world 1.3.0 => 1.3.1
  ```

  ```bash Bump major theme={null}
  uv version --bump major
  # hello-world 1.3.1 => 2.0.0
  ```
</CodeGroup>

### Pre-release versions

<CodeGroup>
  ```bash Move to beta theme={null}
  uv version --bump patch --bump beta
  # hello-world 1.3.0 => 1.3.1b1
  ```

  ```bash Move to alpha theme={null}
  uv version --bump major --bump alpha
  # hello-world 1.3.0 => 2.0.0a1
  ```

  ```bash Increment beta theme={null}
  uv version --bump beta
  # hello-world 1.3.0b1 => 1.3.0b2
  ```

  ```bash Move to stable theme={null}
  uv version --bump stable
  # hello-world 1.3.1b2 => 1.3.1
  ```
</CodeGroup>

### Set specific component values

```bash theme={null}
uv version --bump patch --bump dev=66463664
# hello-world 0.0.1 => 0.0.2.dev66463664
```

<Note>
  By default, `uv version` performs lock and sync. Use `--frozen` to prevent both, or `--no-sync` to only prevent syncing.
</Note>

## Publishing your package

<Steps>
  <Step title="Get credentials">
    Set PyPI credentials using environment variables or command options:

    <CodeGroup>
      ```bash Token (recommended) theme={null}
      export UV_PUBLISH_TOKEN="your-token"
      ```

      ```bash Username and password theme={null}
      export UV_PUBLISH_USERNAME="your-username"
      export UV_PUBLISH_PASSWORD="your-password"
      ```
    </CodeGroup>

    <Note>
      PyPI no longer supports username/password authentication. Generate a token instead, which is equivalent to `--username __token__` with the token as password.
    </Note>
  </Step>

  <Step title="Publish to PyPI">
    ```bash theme={null}
    uv publish
    ```

    For GitHub Actions or other Trusted Publishers, [add a trusted publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) to PyPI — no credentials needed!
  </Step>
</Steps>

<Tip>
  A complete guide to publishing from GitHub Actions to PyPI is available in the [GitHub Guide](integration/github.md#publishing-to-pypi).
</Tip>

### Publishing to custom indexes

Configure custom indexes in `pyproject.toml`:

```toml theme={null}
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true
```

Then publish:

```bash theme={null}
uv publish --index testpypi
```

<Note>
  When using `uv publish --index <name>`, `pyproject.toml` must be present (e.g., include a checkout step in CI).
</Note>

### Handling failed uploads

`uv publish` retries failed uploads, but if some files upload and others fail:

<AccordionGroup>
  <Accordion title="With PyPI">
    Retry the same command — existing identical files are ignored:

    ```bash theme={null}
    uv publish
    ```
  </Accordion>

  <Accordion title="With other registries">
    Use `--check-url` with the index URL:

    ```bash theme={null}
    uv publish --check-url https://registry.example.com/simple/
    ```

    uv skips uploading files identical to those in the registry and handles parallel upload races.
  </Accordion>
</AccordionGroup>

<Warning>
  Existing files must match exactly. This prevents accidentally publishing distributions with different contents for the same version.
</Warning>

## Uploading attestations

uv supports uploading [attestations](https://peps.python.org/pep-0740/) to registries like PyPI.

<Note>
  `uv publish` doesn't generate attestations — create them separately before publishing.
</Note>

uv automatically discovers and matches attestations:

```bash theme={null}
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
```

<Warning>
  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.
</Warning>

## Testing your package

After publishing, test that it installs correctly:

```bash theme={null}
uv run --with <PACKAGE> --no-project -- python -c "import <PACKAGE>"
```

The `--no-project` flag avoids using your local project directory.

<Tip>
  If you just published, add `--refresh-package <PACKAGE>` to avoid cached versions.
</Tip>

## Complete workflow examples

### First-time package release

<Steps>
  <Step title="Prepare project">
    Ensure you have:

    * A configured build system
    * Complete project metadata
    * Tests passing
  </Step>

  <Step title="Set version">
    ```bash theme={null}
    uv version 1.0.0
    ```
  </Step>

  <Step title="Build distributions">
    ```bash theme={null}
    uv build --no-sources
    ```
  </Step>

  <Step title="Test build">
    ```bash theme={null}
    ls dist/
    # Verify both .whl and .tar.gz exist
    ```
  </Step>

  <Step title="Publish to TestPyPI first">
    ```bash theme={null}
    uv publish --index testpypi
    ```
  </Step>

  <Step title="Test installation">
    ```bash theme={null}
    uv run --with my-package --no-project --index https://test.pypi.org/simple/ -- python -c "import my_package"
    ```
  </Step>

  <Step title="Publish to PyPI">
    ```bash theme={null}
    uv publish
    ```
  </Step>
</Steps>

### Releasing a new version

<Steps>
  <Step title="Make your changes">
    Develop and test new features.
  </Step>

  <Step title="Bump version">
    ```bash theme={null}
    uv version --bump minor  # or major, patch
    ```
  </Step>

  <Step title="Update changelog">
    Document changes in `CHANGELOG.md`.
  </Step>

  <Step title="Build and publish">
    ```bash theme={null}
    uv build --no-sources
    uv publish
    ```
  </Step>

  <Step title="Tag release">
    ```bash theme={null}
    git tag v$(uv version --short)
    git push --tags
    ```
  </Step>
</Steps>

### Publishing from GitHub Actions

<Steps>
  <Step title="Set up PyPI trusted publisher">
    Configure at [https://pypi.org/manage/account/publishing/](https://pypi.org/manage/account/publishing/)
  </Step>

  <Step title="Create workflow">
    ```yaml .github/workflows/publish.yml theme={null}
    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
    ```
  </Step>

  <Step title="Create a release">
    GitHub Actions automatically builds and publishes.
  </Step>
</Steps>

## Best practices

<AccordionGroup>
  <Accordion title="Always test before publishing">
    Test on TestPyPI first:

    ```bash theme={null}
    uv publish --index testpypi
    ```

    This catches issues without affecting your PyPI release.
  </Accordion>

  <Accordion title="Use semantic versioning">
    Follow [SemVer](https://semver.org/):

    * Major: Breaking changes
    * Minor: New features, backward compatible
    * Patch: Bug fixes

    ```bash theme={null}
    uv version --bump minor  # 1.2.3 => 1.3.0
    ```
  </Accordion>

  <Accordion title="Verify build contents">
    Check what's included in your distributions:

    ```bash theme={null}
    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.
  </Accordion>

  <Accordion title="Automate with CI/CD">
    Use GitHub Actions or similar for:

    * Automated testing
    * Building on multiple platforms
    * Publishing on release

    See the [GitHub integration guide](integration/github.md).
  </Accordion>

  <Accordion title="Document your releases">
    Maintain a `CHANGELOG.md` and create GitHub releases with notes.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="PyPA build guide" icon="box" href="https://packaging.python.org/en/latest/guides/section-build-and-publish/">
    Official Python packaging guides
  </Card>

  <Card title="Integration guides" icon="plug" href="./integration/index.md">
    Integrate uv with other tools
  </Card>

  <Card title="GitHub integration" icon="github" href="./integration/github.md">
    CI/CD with GitHub Actions
  </Card>

  <Card title="Build systems" icon="gear" href="../concepts/projects/config.md#build-systems">
    Configure build systems
  </Card>
</CardGroup>
