Blog

How to install Python packages from an internal package registry with uv

08 Jan, 2025
Xebia Background Header Wave

If you’ve published a Python package to a private index, you want your users to be able to install it. But this requires some setup. In this blogpost, we’ll show how to set up an internal package dependency with uv.

For our index, we’ll be using Gitlab’s package registry. But many of these steps will also apply when you are using another index.

Install uv

You can install uv in a couple different ways. If you already have Python you can use pipx: pipx install uv.

If you want to install it as a standalone, you can use curl:

curl -LsSf https://astral.sh/uv/install.sh | sh

You can find more instructions in the uv repository.

Adding a Python package to your Gitlab package registry

To publish a Python package to your Gitlab package registry, follow the steps in our other blogpost. When you have a package published, you can continue to the next step.

Set up your credentials

Create a personal access token (for Gitlab)

To install our internal package, we need to configure our credentials. These instructions are specific to Gitlab. If you use another index, these instructions may differ.

For installation of packages we will use a Personal Access Token. To make things simple, use the following URL and update GITLAB_HOST to reflect your own Gitlab host:

  1. If you are using gitlab.com, use this url: https://gitlab.com/-/user_settings/personal_access_tokens?name=Package+Registry+Token&scopes=api,read_user,read_registry
  2. If you are using a private gitlab instance, use:
    https://<GITLAB_HOST>/-/user_settings/personal_access_tokens?name=Package+Registry+Token&scopes=api,read_user,read_registry

This URL will fill the form with a name and the required permissions. Open it in your browser. Click on Create personal access token and save your token in a safe place (such as a password manager). You will need it in the next step.

Note that you might want to change the expiration date for your token.

Next, we need to tell uv about our credentials.

Saving credentials on your system

We could tell uv about our credentials every time we want to install from our private registry. But this will become annoying quickly. Therefore, we want to save our credentials on our system.

uv supports the use of a .netrc file. In this file, we can specify credentials per machine. This will allow us to set credentials for a particular host only once.

In your home directory, create a file called .netrc:

touch ~/.netrc

In the file, put the following and modify the values for machine and password:

machine myorganization.gitlab.host
login __token__
password <your_token>

Now it’s time to add our internal package as a dependency.

Add the internal package from the private index as a dependency

Determine your package index url

First, you must determine your index URL. To create the URL, you need to find your host (e.g. mycompany.gitlab.host) and the Project ID.

You can find the project ID on the page of your repository.

gitlab project id

Note that for this blog post, we publish to a project package registry. But it is also possible to publish to a group or organization package registry. You can find more information about that in the Gitlab docs.

Add your dependency with uv

We can now add our internal package. For that, we run a uv add command, and specify the index. We give this index a name as well, so it becomes --index name=url.

  1. When using gitlab.com:
    uv add my-internal-package --index gitlab=https://gitlab.com/api/v4/projects/PROJECT_ID/packages/pypi/simple
  2. When using a private Gitlab instance:
    uv add my-internal-package --index gitlab=https://myorganization.gitlab.host/api/v4/projects/PROJECT_ID/packages/pypi/simple

This command will make sure that my-internal-package will only be installed from the index called gitlab. This prevents a security issue where a package with the exact same name is installed from PyPi.

Debugging errors

If you get an error, be sure to check if you filled in everything correctly. You can run uv in verbose mode by adding -v to your command. This will show more information. Then look for lines containing netrc, like these:

DEBUG Checking netrc for credentials for https://myorganization.gitlab.host/api/v4/...
DEBUG Found credentials in netrc file for https://myorganization.gitlab.host/api/v4/...

What has changed in pyproject.toml?

If your command was successful, uv will have updated the pyproject.toml automatically. It will now look something like this:

dependencies = [
 "my-internal-package==1.0.0",
]

# Lock my-internal-package to our GitLab package registry
[tool.uv.sources]
my-internal-package = { index = "gitlab" }

# Specify where our Gitlab package registry is
[[tool.uv.index]]
name = "gitlab"
url = "https://<GITLAB_HOST>/api/v4/projects/<PROJECT_ID>/packages/pypi/simple"

Conclusion

In this blogpost we have:

  • Set up credentials for our private index in a .netrc file, so they can be reused.
  • Added an internal package to our project dependencies.
  • We’ve told uv to only look for our internal package on our private index.

These steps will hopefully make uv easier to adopt in your organization.


Sources


Photo by Kelli McClintock on Unsplash

Timo Uelen
Timo Uelen is a Machine Learning Engineer at Xebia.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts