No. GitHub Packages supports npm, Docker and other container images, Maven, Gradle, NuGet and RubyGems, but not Python. There’s no PyPI-compatible registry, so you can’t twine upload to it or point pip at it.

It’s one of the most requested GitHub features. The community discussion asking for it has been open since December 2021 with hundreds of upvotes, and a reply in the thread says it’s no longer planned. If your code is on GitHub and you want private Python packages, you need to host them somewhere else.

Your options

Install straight from Git. pip install git+https://github.com/your-org/your-lib.git@v1.2.0 works with no extra service. The downsides show up as you grow: pip can’t resolve version ranges across tags, every install clones the repo, and every consumer (including CI) needs read access to the source repository.

GitLab’s package registry. GitLab has a PyPI-compatible registry on every tier, including free. It makes most sense if you’re already on GitLab. Using it from GitHub means a GitLab project and a second set of tokens. By default it also forwards requests for packages it doesn’t have to pypi.org, which you’ll want to understand if you’re worried about dependency confusion.

AWS CodeArtifact. It works, and it’s a natural choice if you’re already deep in AWS. Expect some IAM setup, and its auth tokens last at most 12 hours, so CI and developer machines need to refresh them with aws codeartifact login.

Self-host. pypiserver and devpi are mature and free. You run, back up and secure them yourself.

A hosted registry. Gemfury, Cloudsmith, JFrog Artifactory and RepoForge all give you a private PyPI index you can publish to from GitHub Actions. The rest of this post uses RepoForge, because it’s ours, but the setup is broadly similar elsewhere.

Publishing from GitHub Actions

You need two repository secrets:

  • REPOFORGE_TOKEN: an API token with push access, created in RepoForge
  • REPOFORGE_HASH_ID: your registry ID, the part after api.repoforge.io/ in your registry URL

Then add .github/workflows/publish.yml. This builds and publishes whenever you push a tag starting with v; the action does the build for you:

name: Publish

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: RepoForge-io/repoforge-publish-action@v1.1.1
        with:
          package_type: python
          api_token: ${{ secrets.REPOFORGE_TOKEN }}
          hash_id: ${{ secrets.REPOFORGE_HASH_ID }}
          package_dir: ./dist

Tag a release and push it:

git tag v1.2.0
git push origin v1.2.0

Installing

Authentication is HTTP basic auth: the username can be anything and the password is your token. Use a read-only token for installs.

pip

pip install your-lib \
  --index-url https://token:${REPOFORGE_TOKEN}@api.repoforge.io/<your-id>/

Or set it once in pip.conf (~/.config/pip/pip.conf on Linux and macOS):

[global]
index-url = https://token:<your-token>@api.repoforge.io/<your-id>/

Enable upstream mirroring on your registry, and this single index URL serves both your private packages and everything from PyPI. Then you don’t need --extra-index-url, which is what makes dependency confusion possible.

uv

In pyproject.toml:

[[tool.uv.index]]
name = "repoforge"
url = "https://api.repoforge.io/<your-id>/"

Credentials come from the environment, so they never end up in the file:

export UV_INDEX_REPOFORGE_USERNAME=token
export UV_INDEX_REPOFORGE_PASSWORD=$REPOFORGE_TOKEN
uv add your-lib

Poetry

poetry source add --priority=supplemental repoforge https://api.repoforge.io/<your-id>/
poetry config http-basic.repoforge token $REPOFORGE_TOKEN
poetry add --source repoforge your-lib

In another GitHub Actions workflow

Add a read-only token as a secret and pass it through the environment:

      - name: Install dependencies
        env:
          REPOFORGE_TOKEN: ${{ secrets.REPOFORGE_READ_TOKEN }}
        run: |
          pip install -r requirements.txt \
            --index-url https://token:${REPOFORGE_TOKEN}@api.repoforge.io/<your-id>/

Try it

RepoForge has a 14-day free trial, no card needed. Create an account and you’ll have a registry URL straight away.