For the complete documentation index, see llms.txt.

Migrating a Python project to Chainguard Libraries

How to migrate an existing Python project to pull dependencies from Chainguard Libraries
  10 min read

Chainguard Libraries for Python provides a curated index of PyPI packages rebuilt from source and verified against Chainguard’s build provenance. Because Chainguard Libraries uses the standard pip simple-index protocol, switching an existing project requires only an index configuration change — no changes to your application code or dependency declarations.

This guide walks through migrating an existing Python project to Chainguard Libraries, covering the two most common setups:

  • Direct access: Your build tool connects directly to libraries.cgr.dev. This option is faster for initial evaluation and smaller-scale setups.
  • Repository manager: Your build tool connects to a repository manager (such as JFrog Artifactory or Sonatype Nexus), which proxies requests to Chainguard Libraries. This option is recommended for teams and organizations.

Prerequisites

Before you begin, you need:

If you do not have an entitlement to Chainguard Libraries for Python yet, run the following command to create an entitlement and enable upstream fallback:

chainctl libraries entitlements create --ecosystems=PYTHON --policy=CHAINGUARD_AND_UPSTREAM

It can take up to 30 minutes for fallback policy changes to take effect.

Authentication prerequisites

For authentication, you need a pull token or the Python keyring provider.

If you plan to use a repository manager, or a non-interactive environment such as CI/CD, you will need a pull token. You must have the owner role or have the libraries.python.pull_token_creator permission to create one.

chainctl auth pull-token create --repository=python --name=my-python-token --ttl=720h

To export environment variables directly:

eval $(chainctl auth pull-token --output env --repository=python --name=my-python-token)

Learn more about creating and managing pull tokens in the Libraries Access documentation.

Note: Do not commit credentials to version control. Store tokens as environment variables or CI secrets, not as literal values in pip.conf, .netrc, or any file that might be checked into a repository.

Step 1: Configure authentication and registry

How you configure the index depends on your environment. Follow the instructions that match your setup.

The https://libraries.cgr.dev/python/ endpoint is also the Chainguard Repository endpoint for Python. By default, it serves only Chainguard-built artifacts. When upstream fallback is enabled for your organization, the same endpoint can also serve requested versions from PyPI under Chainguard security controls.

Direct access

Configure authentication

First, configure authentication using a pull token or the Python keyring provider.

Setting pull token credentials in .netrc is a common approach for individual work stations. Setting authentication in pip.conf is preferred for CI/CD, where you need credentials isolated per project or per pipeline rather than a single shared home-directory file.

Note: .netrc only supports one set of credentials per hostname. Since all Chainguard Libraries are served from libraries.cgr.dev, configuring .netrc for Python will override credentials for any other ecosystem.

First, check for an existing registry entry in .netrc:

grep -A2 "libraries.cgr.dev" ~/.netrc

Remove or correct any stale entry before proceeding.

Then, configure your pull token credentials in .netrc:

cat >> ~/.netrc << EOF
machine libraries.cgr.dev
login ${CHAINGUARD_PYTHON_IDENTITY_ID}
password ${CHAINGUARD_PYTHON_TOKEN}
EOF
chmod 600 ~/.netrc

Point your build tool at Chainguard

Next, point your build tool at the Chainguard index.

Note that Chainguard publishes both standard and remediated Python indexes. Remediated versions use a +cgr.N local-version suffix, and Python package managers treat those as compatible higher-precedence replacements for the base version. In addition, CUDA-enabled Python libraries use separate CUDA-specific indexes such as https://libraries.cgr.dev/cu128/simple/, and they are not dependency-complete for NVIDIA toolkit components.

If you configured authentication via .netrc or the Python keyring provider, run the following commands to set the remediated repository first, then the simple repository:

pip config set global.index-url https://libraries.cgr.dev/python-remediated/simple/
pip config set global.extra-index-url https://libraries.cgr.dev/python/simple/

If you configured authentication via pull token credentials in pip.conf, this step is already done; the index-url you set in .pip/pip.conf already points at Chainguard with credentials embedded.

Repository manager

If your organization uses a repository manager, configure Chainguard Libraries as an upstream source in that proxy first. Follow the global configuration documentation for your repository manager.

Once configured, point your build tool at your repository manager URL instead of libraries.cgr.dev directly. In this setup, the credentials are your repository manager credentials — not a Chainguard pull token.

pip config set global.index-url https://<your-repo-manager-url>/repository/python-all/simple/

Example URLs by repository manager:

Repository managerURL pattern
JFrog Artifactoryhttps://example.jfrog.io/artifactory/api/pypi/python-all/simple/
Sonatype Nexushttps://repo.example.com:8443/repository/python-all/simple/
Google Artifact Registryhttps://<location>-python.pkg.dev/<project>/python-all/simple/
Cloudsmithhttps://dl.cloudsmith.io/.../<org>/python-all/python/simple/

For authentication details specific to your repository manager, refer to the global configuration documentation.

Step 2: Update your lockfile

Your existing lockfile or hash-pinned requirements.txt contains checksums generated against packages downloaded from PyPI or through your repository manager. Because Chainguard rebuilds packages from verified source as well as providing security controls for upstream artifacts, checksums differ even for identical version numbers. If your file uses --hash entries or --require-hashes, installation will fail with a hash mismatch after switching to Chainguard until these are updated.

Use chainctl libraries update-hashes to rewrite only the integrity hashes in your existing lockfile or requirements file to match Chainguard’s artifacts, without re-resolving your dependency graph. Supported formats include requirements.txt, poetry.lock, uv.lock, pdm.lock, Pipfile.lock, and pylock.toml.

Run the following command to auto-detect and update the lockfile in the current project:

chainctl libraries update-hashes

Or specify the lockfile when running the command. For example:

chainctl libraries update-hashes requirements.txt

When using a repo manager, run a command similar to the following:

chainctl libraries update-hashes
  --registry-url https://repo.example.com/repository/python-all/
  --token "$REPO_TOKEN"

By default, Chainguard hashes are appended alongside your existing hashes rather than replacing them. If your installer fails on a dual-hash entry, use the --replace flag.

Alternative: Regenerate the lockfile

Regenerating is not recommended as a first approach. Dependency resolvers pick the newest version satisfying your constraints, which may not yet be available from Chainguard, and may also introduce unrelated dependency upgrades. Expand the following section for instructions on regenerating lockfiles.

Delete and regenerate a lockfile

If you have a specific reason to regenerate - for example, if you are intentionally refreshing your dependency versions - use the following commands:

pip:

pip-compile --generate-hashes --index-url https://libraries.cgr.dev/python/simple/ requirements.in

uv:

rm -f uv.lock  
uv sync

Poetry 1.x:

poetry lock --no-update

Poetry 2.x:

poetry lock

Step 3: Clear caches

Reinstalling after switching indexes can silently reuse a cached artifact from your previous index, with no error indicating this happened. To avoid this, clear caches.

pip cache purge

Also invalidate cached packages in any repository manager that previously proxied PyPI, and rebuild containers without cached layers if your Dockerfile installs dependencies during build:

docker build --no-cache .

Step 4: Reinstall dependencies

Reinstall dependencies and confirm that the lockfile reflects Chainguard as the source.

pip install --force-reinstall --no-cache-dir -r requirements.txt

Step 5: Verify your libraries

After reinstalling, you can use chainctl to verify which dependencies are built by Chainguard. When upstream fallback is enabled, libraries that aren’t built by Chainguard are subject to Chainguard’s security controls.

chainctl libraries verify --detailed ./.venv/

A successful result shows what percentage of your project’s dependencies were built by Chainguard.

For full details on verification options and output, check out Verification: Analyze Python packages.

Step 6: Commit and roll out

Commit the updated lockfile and any non-sensitive configuration changes. Apply the same index, cache, and hash-update steps to other developer workstations and build servers as you migrate them, including CI/CD platforms and any infrastructure that builds the application or installs dependencies.

For organization-wide rollout using a repository manager, refer to the global configuration documentation.

Packages not available in Chainguard Libraries

Chainguard Libraries covers a large and growing collection of PyPI packages, but not every package or version is available. If a package is missing, your install will fail with a 404 unless you have configured upstream fallback.

With upstream fallback enabled, packages not yet available from Chainguard are proxied from PyPI, subject to Chainguard’s security controls. Confirm your current policy with:

chainctl libraries entitlements list

For repository manager setups, Chainguard recommends using the configurable fallback rather than configuring a separate public registry fallback in your repository manager, to preserve Chainguard’s security controls.

Learn more about upstream fallback configurations in the Libraries Overview.

Troubleshooting

“403 Forbidden” from the index despite a working keyring

Check for an old or incorrectly scoped .netrc entry for libraries.cgr.dev, which silently takes priority over keyring authentication. Run with full verbosity to confirm the credential source:

pip install -vvv <package> 2>&1 | grep -B2 -A2 "credentials\|40[13]"

A pinned dependency shows “not verified” after running chainctl libraries verify

Check your install output for a local wheel build (Building wheel for <package>), which indicates Chainguard has only a source distribution for that specific pinned version.

Next steps

  • To apply this configuration across your whole organization using a repository manager, refer to the global configuration documentation.
  • To verify downloaded packages were built by Chainguard, refer to the verification documentation.
  • For full per-tool configuration reference, refer to the build configuration documentation.

Last updated: 2026-07-31 18:47