docs/en/enterprise/guides/private-package-registry.mdx
If your project depends on internal or proprietary Python packages hosted on a private registry rather than the public PyPI, you'll need to:
CrewAI AMP uses UV for dependency resolution and installation.
UV supports authenticated private registries through pyproject.toml configuration combined
with environment variables for credentials.
Three pieces work together in your pyproject.toml:
Add the private package to your [project.dependencies] like any other dependency:
[project]
dependencies = [
"crewai[tools]>=0.100.1,<1.0.0",
"my-private-package>=1.2.0",
]
Register your private registry as a named index under [[tool.uv.index]]:
[[tool.uv.index]]
name = "my-private-registry"
url = "https://pkgs.dev.azure.com/my-org/_packaging/my-feed/pypi/simple/"
explicit = true
Setting explicit = true means UV won't search this index for every package — only the
ones you explicitly map to it in [tool.uv.sources]. This avoids unnecessary queries
against your private registry and protects against dependency confusion attacks.
</Info>
Tell UV which packages should be resolved from your private index using [tool.uv.sources]:
[tool.uv.sources]
my-private-package = { index = "my-private-registry" }
[project]
name = "my-crew-project"
version = "0.1.0"
requires-python = ">=3.10,<=3.13"
dependencies = [
"crewai[tools]>=0.100.1,<1.0.0",
"my-private-package>=1.2.0",
]
[tool.crewai]
type = "crew"
[[tool.uv.index]]
name = "my-private-registry"
url = "https://pkgs.dev.azure.com/my-org/_packaging/my-feed/pypi/simple/"
explicit = true
[tool.uv.sources]
my-private-package = { index = "my-private-registry" }
After updating pyproject.toml, regenerate your lock file:
uv lock
UV authenticates against private indexes using environment variables that follow a naming convention
based on the index name you defined in pyproject.toml:
UV_INDEX_{UPPER_NAME}_USERNAME
UV_INDEX_{UPPER_NAME}_PASSWORD
Where {UPPER_NAME} is your index name converted to uppercase with hyphens replaced by underscores.
For example, an index named my-private-registry uses:
| Variable | Value |
|---|---|
UV_INDEX_MY_PRIVATE_REGISTRY_USERNAME | Your registry username or token name |
UV_INDEX_MY_PRIVATE_REGISTRY_PASSWORD | Your registry password or token/PAT |
See Setting Environment Variables in AMP below. </Warning>
The table below shows the index URL format and credential values for common registry providers. Replace placeholder values with your actual organization and feed details.
| Provider | Index URL | Username | Password |
|---|---|---|---|
| Azure DevOps Artifacts | https://pkgs.dev.azure.com/{org}/_packaging/{feed}/pypi/simple/ | Any non-empty string (e.g. token) | Personal Access Token (PAT) with Packaging Read scope |
| GitHub Packages | https://pypi.pkg.github.com/{owner}/simple/ | GitHub username | Personal Access Token (classic) with read:packages scope |
| GitLab Package Registry | https://gitlab.com/api/v4/projects/{project_id}/packages/pypi/simple/ | __token__ | Project or Personal Access Token with read_api scope |
| AWS CodeArtifact | Use the URL from aws codeartifact get-repository-endpoint | aws | Token from aws codeartifact get-authorization-token |
| Google Artifact Registry | https://{region}-python.pkg.dev/{project}/{repo}/simple/ | _json_key_base64 | Base64-encoded service account key |
| JFrog Artifactory | https://{instance}.jfrog.io/artifactory/api/pypi/{repo}/simple/ | Username or email | API key or identity token |
| Self-hosted (devpi, Nexus, etc.) | Your registry's simple API URL | Registry username | Registry password |
Private registry credentials must be configured as environment variables in CrewAI AMP. You have two options:
<Tabs> <Tab title="Web Interface"> 1. Log in to [CrewAI AMP](https://app.crewai.com) 2. Navigate to your automation 3. Open the **Environment Variables** tab 4. Add each variable (`UV_INDEX_*_USERNAME` and `UV_INDEX_*_PASSWORD`) with its valueSee the [Deploy to AMP — Set Environment Variables](/en/enterprise/guides/deploy-to-amp#set-environment-variables) step for details.
```bash
# .env
OPENAI_API_KEY=sk-...
UV_INDEX_MY_PRIVATE_REGISTRY_USERNAME=token
UV_INDEX_MY_PRIVATE_REGISTRY_PASSWORD=your-pat-here
```
```bash
crewai deploy create
```
To update credentials on an existing deployment, see Update Your Crew — Environment Variables.
When CrewAI AMP builds your automation, the resolution flow works like this:
<Steps> <Step title="Build starts"> AMP pulls your repository and reads `pyproject.toml` and `uv.lock`. </Step> <Step title="UV resolves dependencies"> UV reads `[tool.uv.sources]` to determine which index each package should come from. </Step> <Step title="UV authenticates"> For each private index, UV looks up `UV_INDEX_{NAME}_USERNAME` and `UV_INDEX_{NAME}_PASSWORD` from the environment variables you configured in AMP. </Step> <Step title="Packages install"> UV downloads and installs all packages — both public (from PyPI) and private (from your registry). </Step> <Step title="Automation runs"> Your crew or flow starts with all dependencies available. </Step> </Steps>Symptom: Build fails with 401 Unauthorized or 403 Forbidden when resolving a private package.
Check:
UV_INDEX_* environment variable names match your index name exactly (uppercased, hyphens → underscores).envSymptom: No matching distribution found for my-private-package.
Check:
pyproject.toml ends with /simple/[tool.uv.sources] entry maps the correct package name to the correct index nameuv lock locally with the same credentials to verify resolution worksSymptom: uv lock fails or produces unexpected results after adding a private index.
Solution: Set the credentials locally and regenerate:
export UV_INDEX_MY_PRIVATE_REGISTRY_USERNAME=token
export UV_INDEX_MY_PRIVATE_REGISTRY_PASSWORD=your-pat
uv lock
Then commit the updated uv.lock.