> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cnap.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Private Container Registries

> Configure authentication for private Docker registries to deploy applications with private images

Clusters need credentials to pull private container images. CNAP's registry proxy centralizes this—configure your registry credentials once, and all deployments pull private images without storing credentials on clusters.

All image pulls through the proxy require authentication; you must configure credentials for any registry that you want to proxy, including public registries like Docker Hub.

## Why Use the Registry Proxy?

* **Centralized credentials** - Add your GitHub PAT or registry password once, use it across all clusters
* **Your credentials stay safe** - Upstream registry passwords and tokens never leave CNAP
* **Instant revocation** - Disable access immediately without rotating credentials everywhere
* **Audit trail** - See which deployments pulled which images and when

### For Marketplace Products

When selling products with private images, the proxy is especially valuable:

* **Keep credentials private** - Customers can pull your images without knowing your PAT
* **Per-customer control** - Revoke a customer's access without affecting others
* **No credential sharing** - Your GitHub PAT gives access to all your repos, but customers only get access to what they purchased

## How It Works

CNAP runs a registry proxy at `oci.cnap.tech`. When clusters pull images through this proxy, CNAP:

1. Validates the request using install-scoped credentials
2. Looks up your workspace's upstream registry credentials
3. Forwards the request to the upstream registry with your credentials
4. Streams the response back to the cluster

Your upstream credentials (GitHub PAT, Docker Hub token, etc.) never leave CNAP's infrastructure.

```mermaid theme={null}
flowchart LR
    subgraph cluster["Your Cluster"]
        pod["Pod pulls image<br/>with install token"]
    end

    subgraph cnap["oci.cnap.tech"]
        proxy["Validates token<br/>Adds workspace creds"]
    end

    subgraph upstream["Upstream Registry"]
        registry["ghcr.io / docker.io<br/>ECR / etc."]
    end

    pod --> proxy --> registry
```

## Using the Proxy in Your Images

To pull images through CNAP's proxy, prefix your image references with `oci.cnap.tech/`:

```yaml theme={null}
# Original image reference
image: ghcr.io/your-org/your-app:v1.0.0

# Through CNAP proxy
image: oci.cnap.tech/ghcr.io/your-org/your-app:v1.0.0
```

The format is: `oci.cnap.tech/{upstream-registry}/{repository}:{tag}`

### Marketplace Products

When building products for the marketplace, reference your private images through the proxy in your Helm chart's `values.yaml`:

```yaml theme={null}
# values.yaml
image:
  repository: oci.cnap.tech/ghcr.io/your-org/your-app
  tag: v1.0.0
```

This ensures customers can deploy your product without needing access to your private registry.

### GitHub Repository Deployments

When deploying from GitHub repositories, your workflow pushes to GHCR normally—CNAP handles the proxy routing automatically. Configure workspace credentials in **Settings → Registries** with a PAT that has `read:packages` scope, and CNAP will detect image references in your Helm values and rewrite them to use the proxy at install time.

<Card title="GitHub Actions Workflows" icon="github" href="/tools/github-actions">
  See how to set up automated builds that push to GHCR and notify CNAP
</Card>

<Tip>
  You can control this behavior per-template with the **Registry Proxy Mode** setting:

  * **Auto** (default) - Only proxy images from registries with configured credentials
  * **Always** - Proxy all images from known registries (ghcr.io, docker.io, etc.)
  * **Never** - Don't rewrite any image URLs
</Tip>

## Setting Up Registry Credentials

<Steps>
  <Step title="Open Registry Settings">
    Navigate to **Settings → Registries** in your workspace. This is where you manage all registry credentials for the workspace.
  </Step>

  <Step title="Add a Registry">
    Click **Add Registry** and enter:

    * **Name** - A friendly name to identify this registry (e.g., "Production Docker Hub")
    * **Registry URL** - The registry hostname (e.g., `docker.io`, `ghcr.io`, `123456789.dkr.ecr.us-east-1.amazonaws.com`)
    * **Authentication type** - Choose based on your registry's requirements
  </Step>

  <Step title="Configure Authentication">
    Enter your credentials based on the authentication type:

    <Tabs>
      <Tab title="Username & Password">
        Standard authentication used by most registries:

        * **Username** - Your registry username
        * **Password** - Your registry password or access token

        Used by: Docker Hub, Harbor, Nexus, JFrog Artifactory
      </Tab>

      <Tab title="Token">
        Token-based authentication for registries that use personal access tokens:

        * **Token** - Your personal access token with read permissions

        Used by: GitHub Container Registry (ghcr.io), GitLab Container Registry
      </Tab>

      <Tab title="OAuth">
        OAuth2 client credentials for enterprise registries:

        * **Client ID** - Your OAuth client ID
        * **Client Secret** - Your OAuth client secret
        * **Token URL** - The OAuth token endpoint

        Used by: Some enterprise registry configurations
      </Tab>
    </Tabs>
  </Step>

  <Step title="Test and Save">
    CNAP validates your credentials before saving. If validation fails, check that your credentials have pull access to the registry.
  </Step>
</Steps>

## Supported Registries

CNAP has built-in support for popular registries:

| Registry                      | URL                                        | Auth Type         | Notes                                                    |
| ----------------------------- | ------------------------------------------ | ----------------- | -------------------------------------------------------- |
| **Docker Hub**                | `docker.io`                                | Username/Password | Use access token instead of password for better security |
| **GitHub Container Registry** | `ghcr.io`                                  | Token             | Use a PAT with `read:packages` scope                     |
| **Amazon ECR**                | `<account>.dkr.ecr.<region>.amazonaws.com` | AWS Credentials   | Access Key ID, Secret Access Key, and Region             |
| **Google Container Registry** | `gcr.io`                                   | Service Account   | Username `_json_key`, password is service account JSON   |
| **Azure Container Registry**  | `<name>.azurecr.io`                        | Username/Password | Use service principal or admin credentials               |
| **GitLab Container Registry** | `registry.gitlab.com`                      | Token             | Use a PAT or deploy token with `read_registry` scope     |
| **Quay.io**                   | `quay.io`                                  | Token             | Use robot accounts for automation                        |

For other OCI-compliant registries (Harbor, JFrog Artifactory, Nexus, etc.), use the **Custom Registry** option with basic authentication.

<Tip>
  For production use, create dedicated service accounts or robot accounts with read-only access rather than using personal credentials.
</Tip>

## Managing Access

### Workspace-Level Credentials

Registry credentials are configured at the workspace level:

* **All deployments in the workspace** can access images from configured registries
* **Different workspaces** can have different registry configurations
* **Credential updates** automatically apply to all deployments in the workspace

This lets you separate production and development registries using different workspaces, or share credentials across all products in a workspace.

### Syncing Credentials to Existing Deployments

When you add or update registry credentials, existing deployments need to be synced to use them. You can:

* **Sync all deployments** - In Settings → Registries, click "Sync All" to push credentials to all active deployments
* **Sync individual deployments** - On a deployment's detail page, click "Re-sync Registry Access" to update that specific deployment

New deployments automatically receive registry access during installation.

### Revoking Access

To revoke access to private images:

1. **Disable a credential** - Toggle the credential to disabled in Settings → Registries. This immediately invalidates all install tokens using that credential. Pods will fail to pull images until the credential is re-enabled.

2. **Delete a credential** - Permanently removes the credential. Existing install tokens become invalid immediately.

<Warning>
  Disabling or deleting credentials takes effect immediately. Running pods won't be affected, but any new pod starts or image pulls will fail until valid credentials are configured.
</Warning>

### Per-Install Tokens

Each deployment (install) receives a unique token for authenticating with the registry proxy. This means:

* Revoking one deployment's access doesn't affect others
* You can track which deployment is pulling which images
* Deleting a deployment automatically revokes its registry token

## Security

CNAP's registry proxy provides several security advantages over distributing credentials directly:

<AccordionGroup>
  <Accordion title="Credentials Never Leave CNAP">
    Your upstream registry credentials (GitHub PAT, Docker Hub token, etc.) are stored in CNAP and never sent to clusters. Clusters receive install-scoped tokens that only work with CNAP's proxy—they can't be used directly against upstream registries.
  </Accordion>

  <Accordion title="Install-Scoped Tokens">
    Each deployment receives a unique token. If a cluster is compromised, you can revoke just that deployment's token without affecting others.
  </Accordion>

  <Accordion title="Instant Revocation">
    Disable a workspace credential to immediately block all image pulls. No need to rotate tokens across multiple clusters or wait for caches to expire.
  </Accordion>

  <Accordion title="Access Control">
    Only workspace members can view or modify registry configurations. Credentials are never displayed after saving—only the registry name and URL are visible.
  </Accordion>

  <Accordion title="Audit Logging">
    CNAP logs all image pull operations, giving you visibility into which images are being accessed, by which deployment, and when.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Images fail to pull">
    **Common causes:**

    * Credentials don't have pull access to the repository
    * Registry URL doesn't match the image reference in your manifest
    * Token or password has expired
    * Image URL not using the proxy format (`oci.cnap.tech/...`)

    **Solution:**

    1. Verify your credentials work by testing them locally with `docker login`
    2. Check that your image references use the proxy format: `oci.cnap.tech/{upstream}/{repo}:{tag}`
    3. Update credentials in CNAP if they've expired
  </Accordion>

  <Accordion title="Deployment not synced">
    **Symptoms:** Newly added credentials aren't working for existing deployments.

    **Solution:** Existing deployments need to be synced after adding credentials. Go to the deployment's detail page and click "Re-sync Registry Access", or use "Sync All" in Settings → Registries.
  </Accordion>

  <Accordion title="Authentication errors">
    **Common causes:**

    * Incorrect username format (some registries require specific formats)
    * Token missing required scopes
    * OAuth client misconfiguration

    **Solution:** Check the registry's documentation for the correct authentication format and required permissions. For GitHub, ensure your PAT has `read:packages` scope.
  </Accordion>

  <Accordion title="Rate limiting">
    **Common causes:**

    * Registry-specific rate limits on your credentials
    * Docker Hub rate limits for authenticated users

    **Solution:** Ensure your credentials have appropriate permissions and haven't exceeded rate limits. For Docker Hub, authenticated users have higher rate limits than anonymous access. If you're hitting limits, consider using a different registry or upgrading your account tier.
  </Accordion>
</AccordionGroup>

## Related Topics

* [GitHub Actions Workflows →](/tools/github-actions) - Automate builds and deploy from GitHub
* [Workspaces →](/workspaces) - Understand how workspaces organize your resources
* [Application Deployments →](/deployments) - Learn how applications are installed
* [App Sources →](/app-sources) - Configure where your applications come from
