Configuring pip to use private registry credentials isn’t about telling pip "use this username and password"; it’s about teaching it to trust a specific server and then providing credentials when that server asks for them.

Let’s imagine you’re trying to install a package from your company’s internal PyPI mirror, pypi.mycompany.com.

pip install my-internal-package

If that registry requires authentication, you’ll likely see an error like this:

HTTPError: 401 Client Error: Unauthorized for url: https://pypi.mycompany.com/simple/my-internal-package/

This isn’t pip failing to send credentials; it’s the server saying, "I don’t know who you are, prove yourself."

Here’s how to make pip play nice with authenticated private registries:

1. The ~/.pypirc File: Pip’s Credential Wallet

Pip (and setuptools) can be configured to use credentials stored in a file named .pypirc in your home directory. This file uses an INI-like format. The key sections are [distutils] and [pypi].

[distutils]
index-servers =
    my-company-pypi

[my-company-pypi]
username: your_username
password: your_password
  • [distutils]: This section lists the names of your custom index servers. my-company-pypi is an arbitrary name you choose to refer to this registry.
  • [my-company-pypi]: This section contains the actual credentials for the server named my-company-pypi.
    • username: Your username for the private registry.
    • password: Your password for the private registry.

Why this works: When pip needs to access a package from a specific index URL, it checks this .pypirc file. If it finds a matching index-server entry, it uses the provided username and password to authenticate.

2. Pip’s Command-Line Arguments: Temporary Credentials

For one-off installations or scripting, you can pass credentials directly on the command line. This is generally less secure as it can expose credentials in shell history or process lists.

pip install --index-url https://your_username:your_password@pypi.mycompany.com/simple/ my-internal-package
  • --index-url: This argument tells pip to use a specific URL as the primary package index.
  • your_username:your_password@: By embedding your username and password in the URL, you’re providing them directly to pip.

Why this works: The http(s)://user:pass@host/ URL format is a standard way to include basic authentication credentials. Pip simply parses this and uses them for the request.

3. Environment Variables: For CI/CD and Automation

Storing credentials directly in scripts or configuration files is often discouraged, especially in automated environments. Environment variables provide a more flexible and secure way to inject credentials.

You can set environment variables that pip recognizes:

  • PIP_INDEX_URL: Similar to --index-url, this sets the primary index URL.
  • PIP_EXTRA_INDEX_URL: Use this to add additional index URLs.
export PIP_INDEX_URL="https://your_username:your_password@pypi.mycompany.com/simple/"
pip install my-internal-package

Alternatively, you can use tools like python-dotenv to load credentials from a .env file into your environment.

# .env file
PIP_INDEX_URL=https://your_username:your_password@pypi.mycompany.com/simple/

Then, in your script:

from dotenv import load_dotenv
import os

load_dotenv()
# pip will now use the PIP_INDEX_URL from the environment
os.system("pip install my-internal-package")

Why this works: Pip checks for these environment variables at runtime. If found, it uses them to construct the request URL, embedding the credentials as if they were passed via --index-url.

4. Using keyring for Secure Storage

For local development, relying on .pypirc can be cumbersome if you frequently change credentials or have many private registries. The keyring library (and its backend, like keyrings.osx, keyrings.windows, or secretstorage for Linux) provides a more secure way to store credentials.

First, install keyring:

pip install keyring

Then, use pip’s built-in support for keyring when configuring your .pypirc file. Instead of the password, you can specify a password-command.

[distutils]
index-servers =
    my-company-pypi

[my-company-pypi]
username: your_username
password-command: python -c "import keyring; print(keyring.get_password('my-company-pypi', 'your_username'))"

When pip needs the password for my-company-pypi, it will execute this password-command. The command uses keyring.get_password to retrieve the password from your operating system’s secure credential store. You’ll be prompted to save the password the first time it’s used.

Why this works: Pip delegates the retrieval of sensitive password information to keyring. This avoids storing plain-text passwords in .pypirc and leverages the OS-level security features for credential management.

5. Trusted Hosts: When the Server’s Identity Matters

Sometimes, the issue isn’t just authentication but also the server’s identity. If your private registry uses a self-signed certificate or an internal CA, pip might refuse to connect because it doesn’t trust the certificate. You can tell pip to trust specific hosts.

Add a trusted-host entry to your .pypirc file:

[distutils]
index-servers =
    my-company-pypi

[my-company-pypi]
username: your_username
password: your_password
trusted-host: pypi.mycompany.com

Alternatively, use the --trusted-host command-line flag:

pip install --index-url https://pypi.mycompany.com/simple/ --trusted-host pypi.mycompany.com my-internal-package

Why this works: This flag tells pip to bypass SSL certificate validation for the specified host. This is useful for internal networks but should be used with caution as it disables a crucial security check.

The Next Hurdle: Package Metadata Conflicts

Once you’ve successfully authenticated and installed packages from your private registry, the next common issue you’ll face is when a package name exists on both your private registry and a public one (like PyPI.org), and you need to ensure pip prioritizes the correct one. This often leads to Package '<package-name>' has no version satisfying requirements ... errors, where pip is trying to resolve a dependency from the wrong source.

Want structured learning?

Take the full Pip course →