Skip to main content

uv publish

Upload distributions to a package index (PyPI or private registries).

Usage

uv publish [OPTIONS] [FILES]...

Description

Upload Python distributions (wheels and source distributions) to a package index. By default, publishes to PyPI, but can be configured to publish to private registries. Supports multiple authentication methods:
  • Username and password
  • API tokens
  • Trusted publishing (GitHub Actions, GitLab CI/CD)
  • Keyring integration

Arguments

[FILES]...

Paths to the files to upload. Accepts glob expressions. Defaults to dist/*. Selects only wheels and source distributions and their attestations, while ignoring other files.
# Publish all files in dist/
uv publish

# Publish specific files
uv publish dist/my-package-1.0.0.tar.gz dist/my_package-1.0.0-py3-none-any.whl

# Publish using glob pattern
uv publish "dist/my-package-*"

Options

Index configuration

--index <INDEX>

The name of an index in the configuration to use for publishing. The index must have a publish-url setting in your pyproject.toml or uv.toml:
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
publish-url = "https://upload.pypi.org/legacy/"
The index url will be used to check for existing files to skip duplicate uploads. With these settings, the following two calls are equivalent:
uv publish --index pypi
uv publish --publish-url https://upload.pypi.org/legacy/ --check-url https://pypi.org/simple
Environment variable: UV_PUBLISH_INDEX

--publish-url <PUBLISH_URL>

The URL of the upload endpoint (not the index URL). Note that there are typically different URLs for index access (e.g., https://.../simple) and index upload. Defaults to PyPI’s publish URL: https://upload.pypi.org/legacy/
# Publish to Test PyPI
uv publish --publish-url https://test.pypi.org/legacy/

# Publish to private registry
uv publish --publish-url https://my-registry.example.com/upload/
Environment variable: UV_PUBLISH_URL

--check-url <CHECK_URL>

Check an index URL for existing files to skip duplicate uploads. This option allows retrying publishing that failed after only some, but not all files have been uploaded, and handles errors due to parallel uploads of the same file. Before uploading, the index is checked. If the exact same file already exists in the index, the file will not be uploaded. If an error occurred during the upload, the index is checked again, to handle cases where the identical file was uploaded twice in parallel. The exact behavior will vary based on the index. When uploading to PyPI, uploading the same file succeeds even without --check-url, while most other indexes error. When uploading to pyx, the index URL can be inferred automatically from the publish URL. The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
uv publish --check-url https://pypi.org/simple
Environment variable: UV_PUBLISH_CHECK_URL

Authentication

-u, --username <USERNAME>

The username for the upload.
uv publish --username myusername
Environment variable: UV_PUBLISH_USERNAME

-p, --password <PASSWORD>

The password for the upload.
uv publish --password mypassword
For security, prefer using environment variables or keyring:
export UV_PUBLISH_PASSWORD=mypassword
uv publish --username myusername
Environment variable: UV_PUBLISH_PASSWORD

-t, --token <TOKEN>

The token for the upload. Using a token is equivalent to passing __token__ as --username and the token as --password.
uv publish --token pypi-AgEIcHlwaS5vcmc...
For security, prefer using environment variables:
export UV_PUBLISH_TOKEN=pypi-AgEIcHlwaS5vcmc...
uv publish
Environment variable: UV_PUBLISH_TOKEN

--trusted-publishing <TRUSTED_PUBLISHING>

Configure trusted publishing. By default, uv checks for trusted publishing when running in a supported environment, but ignores it if it isn’t configured. Supported environments:
  • GitHub Actions
  • GitLab CI/CD
uv publish --trusted-publishing always
Values:
  • always - Require trusted publishing
  • never - Disable trusted publishing
  • automatic - Use if available (default)

--keyring-provider <KEYRING_PROVIDER>

Attempt to use keyring for authentication for remote requirements files. At present, only --keyring-provider subprocess is supported, which configures uv to use the keyring CLI to handle authentication. Defaults to disabled.
uv publish --keyring-provider subprocess
Environment variable: UV_KEYRING_PROVIDER

Upload options

--dry-run

Perform a dry run without uploading files. When enabled, the command will check for existing files if --check-url is provided, and will perform validation against the index if supported, but will not upload any files.
uv publish --dry-run
Useful for testing your publishing configuration.

--no-attestations

Do not upload attestations for the published files. By default, uv attempts to upload matching PEP 740 attestations with each distribution that is published.
uv publish --no-attestations
Environment variable: UV_PUBLISH_NO_ATTESTATIONS

Examples

Publish to PyPI

Using an API token (recommended):
export UV_PUBLISH_TOKEN=pypi-AgEIcHlwaS5vcmc...
uv publish
Or with username and password:
uv publish --username myusername --password mypassword

Publish to Test PyPI

uv publish \
  --publish-url https://test.pypi.org/legacy/ \
  --token pypi-AgEIcHlwaS5vcmc...

Publish to a private registry

Configure in pyproject.toml:
[[tool.uv.index]]
name = "private"
url = "https://my-registry.example.com/simple"
publish-url = "https://my-registry.example.com/upload/"
Then publish:
uv publish --index private --username myuser --password mypass

Publish specific files

uv publish dist/my-package-1.0.0.tar.gz dist/my_package-1.0.0-py3-none-any.whl

Dry run to test configuration

uv publish --dry-run
Output:
Checking dist/my-package-1.0.0.tar.gz
Checking dist/my_package-1.0.0-py3-none-any.whl
Dry run completed successfully (no files uploaded)

Publish with trusted publishing (GitHub Actions)

In your GitHub Actions workflow:
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v3
      - name: Build
        run: uv build
      - name: Publish
        run: uv publish --trusted-publishing always

Check for existing files before upload

uv publish --check-url https://pypi.org/simple
This prevents duplicate upload errors and allows retrying failed uploads.

Publish using keyring

# Store credentials in keyring first
keyring set https://upload.pypi.org/legacy/ __token__

# Publish using keyring
uv publish --keyring-provider subprocess

Use cases

CI/CD publishing workflow

Complete workflow for building and publishing:
# Build distributions
uv build

# Verify build artifacts
ls -lh dist/

# Publish to PyPI
uv publish

Publishing to multiple registries

Publish to Test PyPI first, then to PyPI:
# Test on Test PyPI
uv publish --publish-url https://test.pypi.org/legacy/

# Install and test from Test PyPI
uv pip install --index https://test.pypi.org/simple my-package

# Publish to production PyPI
uv publish

Retry failed uploads

If a publish fails partway through:
uv publish --check-url https://pypi.org/simple
Files already uploaded will be skipped.

Workspace publishing

Publish all workspace packages:
uv build --all-packages
uv publish

Security best practices

Use API tokens instead of passwords

API tokens are more secure and can be scoped:
# Generate a token at https://pypi.org/manage/account/token/
export UV_PUBLISH_TOKEN=pypi-AgEIcHlwaS5vcmc...
uv publish

Use trusted publishing when possible

Trusted publishing eliminates the need for long-lived credentials:
# GitHub Actions
permissions:
  id-token: write

steps:
  - run: uv publish --trusted-publishing always

Store credentials securely

Never commit credentials to version control:
# Use environment variables
export UV_PUBLISH_TOKEN=...

# Or use keyring
keyring set https://upload.pypi.org/legacy/ __token__
uv publish --keyring-provider subprocess

Test with dry run first

Verify configuration before actual upload:
uv publish --dry-run
  • uv build - Build distributions for publishing
  • uv init - Create a new project

Notes

  • By default, uv publishes to PyPI (https://upload.pypi.org/legacy/)
  • API tokens are preferred over username/password authentication
  • Trusted publishing is the most secure option for CI/CD
  • Use --dry-run to test your configuration without uploading
  • PEP 740 attestations are uploaded by default when available
  • Use --check-url to handle duplicate uploads and retry scenarios

Build docs developers (and LLMs) love