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
- A PyPI account
- A GitHub repository containing a Python package with a
pyproject.toml - uv installed (installation guide)
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 Settings → Publishing, 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:
| Field | Value |
|---|---|
| Owner | Your GitHub username or organization |
| Repository name | The repository that will publish the package |
| Workflow name | The filename of the workflow, e.g. publish.yml |
| Environment name | Optional. 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 publishA few things to note:
permissions: id-token: writeis required. This allows the workflow to request an OIDC token from GitHub’s token service. Without it,uv publishhas no credential to present to PyPI.on: releasetriggers the workflow when you create a GitHub release. You can also trigger on tag pushes (on: push: tags: ["v*"]).uv publishdetects the GitHub Actions environment automatically, requests an OIDC token, exchanges it with PyPI for a short-lived upload credential, and publishes. No--tokenflag is needed.- The
environmentblock 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 Releases → Draft 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
- PyPI Trusted Publishers documentation
- uv publishing guide
- GitHub Actions OIDC security hardening
- Why use trusted publishing?
- Setting up GitHub Actions with uv
- Publishing your first Python package to PyPI
Also Mentioned In
Get Python tooling updates
Subscribe to the newsletter