Skip to content

How to Publish to PyPI with Trusted Publishing

Trusted publishing lets GitHub Actions upload packages to PyPI without storing any secrets. Instead of creating an API token and pasting it into your CI settings, you tell PyPI which GitHub repository and workflow are allowed to publish. GitHub Actions then proves its identity to PyPI using an OIDC token that lives only for the duration of the workflow run.

This guide covers GitHub Actions, the most common CI provider for Python projects. PyPI also supports trusted publishing from GitLab CI/CD, Google Cloud, and ActiveState.

Prerequisites

Configure PyPI to trust your repository

Log in to PyPI

Go to pypi.org and sign in.

Open the trusted publisher settings

For an existing project: Navigate to your project, go to SettingsPublishing, and scroll to Add a new publisher.

For a new project that has never been uploaded: Go to your account publishing settings and scroll to Create a new pending publisher. Enter the package name exactly as it will appear on PyPI.

Fill in the GitHub details

Enter the following:

FieldValue
OwnerYour GitHub username or organization
Repository nameThe repository that will publish the package
Workflow nameThe filename of the workflow, e.g. publish.yml
Environment nameOptional. If your workflow uses a GitHub Actions environment, enter its name here.

Save

Click Add. PyPI will now accept uploads from that specific workflow.

Create the publish workflow

Create .github/workflows/publish.yml in your repository:

name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    environment:
      name: pypi
      url: https://pypi.org/p/<YOUR_PACKAGE_NAME>
    steps:
      - uses: actions/checkout@v6
      - uses: astral-sh/setup-uv@v7
      - run: uv build
      - run: uv publish

A few things to note:

  • permissions: id-token: write is required. This allows the workflow to request an OIDC token from GitHub’s token service. Without it, uv publish has no credential to present to PyPI.
  • on: release triggers the workflow when you create a GitHub release. You can also trigger on tag pushes (on: push: tags: ["v*"]).
  • uv publish detects the GitHub Actions environment automatically, requests an OIDC token, exchanges it with PyPI for a short-lived upload credential, and publishes. No --token flag is needed.
  • The environment block is optional but recommended. GitHub environments let you add required reviewers, restrict which branches can deploy, and see deployment history. If you specify an environment here, use the same environment name in your PyPI trusted publisher configuration.

Publish a release

Tag your release

$ git tag v0.1.0
$ git push origin v0.1.0

Create a GitHub release

Go to your repository on GitHub, click ReleasesDraft a new release, select the tag you just pushed, and click Publish release.

Watch the workflow

Open the Actions tab. The publish workflow should start. If everything is configured correctly, uv publish will authenticate via OIDC, upload the built distributions, and the job will complete with a green checkmark.

Verify the upload

On your package’s PyPI page, the upload should show provenance information linking back to the specific GitHub Actions workflow run that produced it. This provenance attestation lets anyone verify that the package was built from the source in your repository.

Revoke old API tokens

If you were previously using an API token to publish, go to your PyPI account settings and delete the token. There is no reason to keep a long-lived secret around once trusted publishing is working.

Troubleshooting

“Token request failed” or 403 errors: Check that permissions: id-token: write is set on the job or workflow. Verify that the workflow filename in your PyPI publisher configuration matches the actual file in .github/workflows/.

Environment mismatch: If you configured an environment name in PyPI, the workflow must use that same environment. If you left the environment field blank in PyPI, do not set an environment in the workflow.

Pending publisher not matching: The package name in the pending publisher configuration must exactly match the name field in your pyproject.toml (PyPI normalizes names, so hyphens and underscores are equivalent).

Reusable workflows: PyPI does not currently support reusable GitHub Actions workflows as the trusted workflow. The workflow file that calls uv publish must be defined directly in your repository.

Learn more

Get Python tooling updates

Subscribe to the newsletter
Last updated on

Please submit corrections and feedback...