# Argo CD: How to Use a private OCI Helm Chart Repository

[Argo CD: How to Use a private OCI Helm Chart Repository](https://iits-consulting.de/en/blog/argocd-how-to-use-a-private-oci-helm-chart-repository)

Alexander Hoeft

2025-03-13T13:25:21.000Z

Learn how to complete an Argo CD Private OCI Helm Repository Setup with Docker, GitLab, and Harbor. Secure Helm charts and automate deployments.

### Step-by-Step Guide to Setting Up a Private OCI Helm Repository with Argo CD, GitOps, Docker, GitLab, and Harbor

> ***Note: ****When you start looking into this topic, you’ll come across countless GitHub issues with various solutions. Some work, some don’t. At first, we were confused by the many conflicting recommendations, so we decided to create a clean and reliable setup for different private Helm repositories. *
> 
> 
> 
> *We = Alexander Hoeft, Philip Siemer, Daniel Schlosser and Artem Lajko*

Fig.0: Deploying Cert-Manager from a Mirrored Private OCI Registry (Harbor)

There are various scenarios where you might need more than just hosting your own Helm charts in a private registry. For example, you may need to mirror and sign Helm charts before making them available in a private repository. This private registry is often used by different customers to deploy signed Helm charts on their respective clusters. In many cases, these clusters don’t have internet access, or policies (e.g., [Kyverno](https://kyverno.io/)) restrict pulling images only from specific Helm chart repositories.

[Starting ](https://docs.docker.com/docker-hub/usage/)[**April 1, 2025**](https://docs.docker.com/docker-hub/usage/)[, Docker will introduce new pull rate limits:](https://docs.docker.com/docker-hub/usage/)

- **Unauthenticated users:** 10 pulls per hour
- **Authenticated users (free account):** 100 pulls per hour
- **Pro, Team, and Business subscribers:** Unlimited pulls (with fair use)

Container image mirroring and Helm chart signing are essential best practices. With Docker’s new pull rate limits, private repositories are now more critical than ever.

Alright, enough theory — let’s dive into the hands-on part and see how to set up **private OCI Helm repositories** across different platforms!

## Difference Between a Private Helm Repository and a Private OCI Helm Repository

When working with Argo CD, there are key differences between a **Helm private repository** and a **Helm OCI private repository**. The distinction isn’t just about the protocol — it also affects how Argo CD integrates these repositories and how secrets need to be configured.

For this guide, we’ll focus **exclusively on OCI-based repositories**, as we consider OCI the industry standard. [OCI support has been available since ](https://helm.sh/docs/topics/registries/)[**Helm 3.8.0**](https://helm.sh/docs/topics/registries/), making it the preferred approach moving forward.

This how you configure it in the Argo CD GUI. Go to Settings -\> Repositories and hit + CONNECT RREPO.

Fig. 1: Argo CD GUI: + CONNECT REPO

Fig. 2: Argo CD GUI: + CONNECT REPO (Enable OCI)

Don’t forget to enable OCI!

When using an **OCI-enabled Helm repository**, the secret in Argo CD looks like this:

```yaml
apiVersion: v1
data:
  enableOCI: dHJ1ZQ==
  name: ZG9ja2VyLW9jaQ==
  password: Z.....
  project: ZGVmYXVsdA==
  type: aGVsbQ==
  url: cmVnaXN0cnktMS5kb2NrZXIuaW8= (registry-1.docker.io)
  username: YXJ0ZW1sYQ==
kind: Secret
metadata:
  annotations:
    managed-by: argocd.argoproj.io
  labels:
    argocd.argoproj.io/secret-type: repository
  name: repo-2984550025
  namespace: argocd
type: Opaque
```

[**What’s Different About OCI in Argo CD?**](https://argo-cd.readthedocs.io/en/latest/user-guide/private-repositories/)

1. **No Protocol Prefix (*****oci://*****) Needed:**

- Unlike traditional Helm repositories (htttps), you **don’t** manually specify the *oci://* protocol in Argo CD.
- Argo CD automatically applies this when working with OCI-based registries.

**2. URL Structure (No Repository Path):**

- The URL field only contains the **registry address** (*e.g., registry-1.docker.io*).
- You **don’t** include the repository path (e.g., */artemla/cert-manager*).
- This is important — see the Docker section below for more details.

**Our Setup**

We’ve tested and validated this setup with:

- **Argo CD Version:***v2.14.3*
- [**Argo CD Helm Chart Version:**](https://artifacthub.io/packages/helm/argo/argo-cd/7.8.7)[*7.8.7*](https://artifacthub.io/packages/helm/argo/argo-cd/7.8.7)
- **Helm CLI Version:***v3.17.0*

Additionally, we’ve confirmed compatibility with older versions of **both Argo CD and Helm**.

Now that we understand the differences, let’s move on to the actual setup!

## Docker OCI: Setting Up a Private OCI Helm Repository with Argo CD

In this section, we’ll go through the full process of:

1. Creating a [**Personal Access Token (PAT)**](https://docs.docker.com/security/for-developers/access-tokens/) in Docker Hub.
2. Pulling the **Cert-Manager v1.17.1 Helm chart** from Jetstack.io.
3. Pushing the chart to a **private Docker Helm OCI registry**.
4. Integrating it into **Argo CD** and deploying an application from the private repository.

Fig. 4: Deploying a Mirrored Cert-Manager Helm Chart via Docker Private OCI Repository

**Step 1: Generate a Personal Access Token (PAT) in Docker Hub**

First, navigate to [**Docker Hub**](https://hub.docker.com/) and create a **Personal Access Token (PAT)**.

Fig. 5: Docker GUI: Create PAT

**Copy the login command** with your PAT and execute it locally:

```bash
docker login -u artemla

Password:
Login Succeeded
```

**Step 2: Pull the Cert-Manager Helm Chart from Jetstack**

Now, let’s pull the **Cert-Manager v1.17.1** Helm chart:

```bash
helm pull --version 1.17.1 --repo https://charts.jetstack.io cert-manager
```

After running this, you should see the **Helm chart package** in your directory:

```plaintext
ls 
cert-manager-v1.17.1.tgz
```

**Step 3: Push the Helm Chart to a Private Docker Helm OCI Repository**

Now, we push the Helm chart to our **private Docker OCI registry**:

```bash
helm push cert-manager-v1.17.1.tgz oci://registry-1.docker.io/artemla
```

### *Important Notes*

*– You only specify the ****registry (artemla)****, ****not**** the full path (/artemla/cert-manager).*
*– The ****chart name is automatically extracted**** from its metadata, so Docker will create the correct repository structure.*

**Check if the push was successful** by looking at the Docker UI:

Fig. 6: Verifying Helm Chart Push to Private OCI Repository

or by running:

```bash
helm pull oci://registry-1.docker.io/artemla/cert-manager --version v1.17.1
```

**Make sure to set your repository to private** under Docker Hub repository settings!

**Step 4: Add the Private Docker OCI Helm Repository to Argo CD**

**4.1 Add via Argo CD UI**

1. Navigate to **Argo CD → Settings → Repositories**.
2. Click **+ CONNECT REPO **and enter the following details:

- **URL:***registry-1.docker.io* (⚠️ No */artemla *at the end)
- **Enable OCI:** (Make sure to check this option)

3. Click **CONNECT**.

Fig. 7: Adding a Docker Private OCI Helm Repository to Argo CD

Fig.8: Don’t forget enable OCI!

Now you should see something like this:

Fig. 9: After successfully added

**4.2 Add via CLI (Imperative Approach)**

Alternatively, you can add the repository using **kubectl** by creating a Kubernetes **Secret**:

```yaml
apiVersion: v1
data:
  enableOCI: dHJ1ZQ==
  name: ZG9ja2VyLW9jaQ==
  password: ZG.......
  project: ZGVmYXVsdA==
  type: aGVsbQ==
  url: cmVnaXN0cnktMS5kb2NrZXIuaW8=
  username: YXJ0ZW1sYQ==
kind: Secret
metadata:
  annotations:
    managed-by: argocd.argoproj.io
  labels:
    argocd.argoproj.io/secret-type: repository
  name: repo-2984550025
  namespace: argocd
type: Opaque
```

**For a declarative approach**, we recommend using [**External Secrets Operator**](https://external-secrets.io/latest/) or [**Sealed Secrets**](https://github.com/bitnami-labs/sealed-secrets) for secret management.

**Step 5: Deploy Cert-Manager from the Private OCI Repository in Argo CD GUI**

1. Navigate to **Argo CD → Applications**.
2. Click **+ NEW APP** and fill in the form as follows:

- **Repository URL:***registry-1.docker.io*
- **Chart Name:***artemla/cert-manager*
- **Version:***v1.17.1*

3. Click **CREATE**.

Fig. 10: Deploying Cert-Manager via Docker Private OCI Helm Repository

**Argo CD should now show an “OutOfSync” status**, meaning it can successfully pull the Helm chart from your private OCI registry.

Fig. 11: Argo CD Application Status After Deploying Cert-Manager

**Application YAML Example**

This is what the corresponding **Argo CD Application** YAML looks like:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cert-manager
  namespace: argocd
spec:
  destination:
    namespace: cert-manager
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: artemla/cert-manager
    repoURL: registry-1.docker.io
    targetRevision: v1.17.1
  sourceType: Helm
```

This works for both *ApplicationSets* and Umbrella Charts.
No need for the *passCredentials* parameter when using OCI repositories.

**Step 6 (Optional): Deploying with ApplicationSets and an Umbrella Helm Chart**

For dynamic deployments, you can use [**ApplicationSets**](https://argo-cd.readthedocs.io/en/stable/user-guide/application-set/) with a [**Cluster Generator**](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators-Cluster/)[:](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators-Cluster/)

```yaml

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cert-manager
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            cert-manager: enabled
        values:
          branch: main
  template:
    metadata:
      name: "{{name}}-cert-manager"
      annotations:
        argocd.argoproj.io/manifest-generate-paths: ".;.."

    spec:
      project: default
      sources:
        - repoURL: https://......
          path: "./cert-manager"
          targetRevision: "{{values.branch}}"

      destination:
        name: "{{name}}"
        namespace: "cert-manager"
      syncPolicy:
        syncOptions:
          - CreateNamespace=false
          - PruneLast=true
```

Or, if you’re working with [**Umbrella Helm Charts**](https://helm.sh/docs/howto/charts_tips_and_tricks/), here’s how you define it:

```yaml
apiVersion: v2
name: cert-manager
description: Umbrella Chart for cert-manager
type: application
version: 0.0.1
dependencies:
  - name: artemla/cert-manager
    version: v1.17.1
    repository: "oci://registry-1.docker.io"
```

**Next Up: Setting Up a Private OCI Helm Repository with GitLab**

Now that we’ve successfully set up a private OCI Helm repository using **Docker**, in the next section, we’ll explore how to do the same with **GitLab**.

## GitLab OCI: Setting Up a Private OCI Helm Repository with Argo CD

In this section, we will:

1. Create an **Access Token** for a GitLab repository.
2. Pull the **Cert-Manager v1.17.1 Helm chart** from Jetstack.io.
3. Push the chart to a **private GitLab Helm OCI registry**.
4. Integrate it into **Argo CD** and deploy an application from the private repository.

Fig. 12: Deploying a Mirrored Cert-Manager Helm Chart via GitLab Private OCI Repository

## Step 1: Create a GitLab Access Token

Navigate to **GitLab** or your **self-hosted GitLab instance**.

1. Go to **Settings → Access Tokens** in your repository.
2. Create a **Project Access Token** with the necessary permissions.

like:

Fig. 13: GitLab GUI: Create Project Access Token

**Step 2: Pull the Cert-Manager Helm Chart from GitHub**

Run the following command to pull the **Cert-Manager v1.17.1 Helm chart**:

```bash
helm pull --version 1.17.1 --repo https://charts.jetstack.io cert-manager
```

After execution, verify that the chart package is in your directory:

```plaintext
ls 
cert-manager-v1.17.1.tgz
```

**Step 3: Log in and Push the Helm Chart to a Private GitLab OCI Registry**

Log in to the GitLab registry using the access token and push the Helm chart to the private GitLab OCI repository::

```bash
helm registry login registry.gitlab.your-domain.tech --username git
....

helm push cert-manager-v1.17.1.tgz oci://registry.gitlab.your-domain.tech/private/infrastructure-charts
```

To confirm the push was successful, try pulling the chart:

```bash
helm pull oci://registry.gitlab.your-domain.tech/private/infrastructure-charts/cert-manager --version v1.17.1
```

**Step 4: Add the Private GitLab OCI Helm Repository to Argo CD**

**4.1 Add via Argo CD UI**

1. Navigate to **Argo CD → Settings → Repositories**.
2. Click **+ CONNECT REPO **and enter the following details:

- **URL:**` registry.gitlab.your-domain.tech `
- **Enable OCI:** Make sure this option is checked.

3. Click **CONNECT**.

Fig. 14: Adding a GitLab Private OCI Helm Repository to Argo CD

**4.2 Add via CLI (Imperative Approach)**

You can also add the repository using **kubectl** by creating a Kubernetes **Secret**:

```yaml
apiVersion: v1
data:
  enableOCI: dHJ1ZQ==
  name: aWl0cy1vY2k=
  password: Z.....
  project: ZGVmYXVsdA==
  type: aGVsbQ==
  url: c.....
  username: Z2l0
kind: Secret
metadata:
  annotations:
    managed-by: argocd.argoproj.io
  labels:
    argocd.argoproj.io/secret-type: repository
  name: repo-1906426995
  namespace: argocd
type: Opaque
```

For a **declarative setup**, it is recommended to use **External Secrets Operator** or **Sealed Secrets** for secret management.

**Step 5: Deploy Cert-Manager from the Private OCI Repository in Argo CD**

1. Navigate to **Argo CD → Applications**.
2. Click **+ NEW APP** and fill in the form as follows:

- **Repository URL:**` regist `*ry.gitlab.your-domain.tech*
- **Chart Name:***private/infrastructure-charts/cert-manager*
- **Version:***v1.17.1*

3. Click **CREATE**.

Argo CD should now show an *“OutOfSync”* status, indicating it successfully retrieved the Helm chart from the private OCI registry.

Fig. 15: Deploying Cert-Manager via GitLab Private OCI Helm Repository

**Application YAML Example**

This is what the corresponding **Argo CD Application** YAML looks like:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cert-manager
  namespace: argocd
spec:
  destination:
    namespace: cert-manager
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: private/infrastructure-charts/cert-manager
    repoURL: registry.gitlab.iits.tech
    targetRevision: v1.17.1
```

**Next: Setting Up a Private OCI Helm Repository with Harbor**

Now that we have successfully configured a private OCI Helm repository using **GitLab**, the next section will focus on **Harbor**.

## Harbor OCI: Setting Up a Private OCI Helm Repository with Argo CD

In this section, we will:

1. Create a **Robot Account** in Harbor.
2. Pull the **Cert-Manager v1.17.1 Helm chart** from Jetstack.
3. Push the chart to a **private Harbor OCI registry**.
4. Integrate it into **Argo CD** and deploy an application from the private repository.

Fig. 16: Deploying a Mirrored Cert-Manager Helm Chart via Harbor Private OCI Repository

**Step 1: Create a Robot Account in Harbor**

1. Open the **Harbor UI** and navigate to your **project**.
2. Select **Robot Accounts** and create a new **access token**.
3. Assign the necessary permissions, such as **create, delete, list, and update** for repositories.

Like:

Fig. 17: Create Robot Account

Fig. 18: Create Robot Account (Part 2)

Step 2: Pull the Cert-Manager Helm Chart from Jetstack

Download the **Cert-Manager v1.17.1 Helm chart** using Helm:

```bash
helm pull --version 1.17.1 --repo https://charts.jetstack.io cert-manager
```

After the command executes, confirm that the Helm chart is in your directory:

```plaintext
ls 
cert-manager-v1.17.1.tgz
```

**Step 3: Log in and Push the Helm Chart to a Private Harbor OCI Registry**

Log in to your Harbor registry using the **Robot Account** credentials and push the Helm chart to the private Harbor OCI repository::

```bash
helm registry login  registry.your-domain.cloud/projectname --username 'robot$project-harbor-oci..'
....

helm push cert-manager-v1.17.1.tgz oci://registry.your-domain.cloud/projectname
```

To verify the push, try pulling the chart:

```bash
helm pull oci://registry.your-domain.cloud/projectname/cert-manager --version v1.17.1
```

## Step 4: Add the Private Harbor OCI Helm Repository to Argo CD

## 4.1 Add via Argo CD UI

1. Navigate to **Argo CD → Settings → Repositories**.
2. Click **+ CONNECT REPO **and enter the following details:

- **URL:**` registry.your-do `*main.cloud/projectname*
- **Enable OCI:** Make sure this option is checked.

3. Click **CONNECT**.

Unlike Docker and GitLab, the **project name is included in the URL**.

Fig. 19: Connecting a Harbor Private OCI Helm Repository to Argo CD

**4.2 Add via CLI (Imperative Approach)**

You can also add the repository using **kubectl** by creating a Kubernetes **Secret**:

```yaml
apiVersion: v1
data:
  enableOCI: dHJ1ZQ==....
  name: b2NpL.....
  password: Mk
  project: ZGVmYXVsdA==
  type: aGVsbQ==....
  url: cmVn
  username: cm9ib3Qkc....
kind: Secret
metadata:
  annotations:
    managed-by: argocd.argoproj.io
  labels:
    argocd.argoproj.io/secret-type: repository
  name: repo-1876401384
  namespace: argocd
type: Opaque
```

For a **declarative setup**, it is recommended to use **External Secrets Operator** or **Sealed Secrets** for secret management.

**Step 5: Deploy Cert-Manager from the Private OCI Repository in Argo CD**

1. Navigate to **Argo CD → Applications**.
2. Click **+ NEW APP** and fill in the form as follows:

- **Repository URL:***registry.your-domain.cloud/projectname*
- **Chart Name:***cert-manager*
- **Version:***v1.17.1*

3. Click **CREATE**.

Unlike Docker or GitLab, in Harbor, the **project name is explicitly included in the repository path**. You just need add the Chart like:

Fig. 20: Deploying an Application via Harbor Private OCI Helm Repository in Argo CD

**Application YAML Example**

This is what the corresponding **Argo CD Application** YAML looks like:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cert-manager
  namespace: argocd
spec:
  destination:
    namespace: cert-manager
    server: https://kubernetes.default.svc
  project: default
  source:
    chart: cert-manager
    repoURL: registry.your-domain.cloud/projectname
    targetRevision: v1.17.1
```

## Final Thoughts

This guide covers how to set up **private OCI Helm repositories** in **Docker, GitLab, and Harbor** for **Argo CD deployments**.
By following this guide, your **Argo CD Private OCI Helm Repository Setup** will be optimized for security, automation, and scalability.

All examples work with **Applications, ApplicationSets, and Umbrella Helm Charts**.
For self-hosted projects, we personally recommend **Harbor** or **GitLab** as OCI registries.

This should help regardless of which private OCI registry you choose.

## Thank You!

This blog post wouldn’t have been possible without the contributions and expertise of the following people:

**Authors:**

- [**Alexander Hoeft**](https://www.linkedin.com/in/alexander-h-2a9802347/)
- [**Artem Lajko**](https://www.linkedin.com/in/lajko/)

**Engineering Support:**

- [**Philip Siemer**](https://www.linkedin.com/in/philip-siemer-a512a0145/)
- **Daniel Schlosser**

A huge thank you to everyone involved in making this guide as clear, practical, and valuable as possible!

Visit our other [blogs!](https://iits-consulting.de/blog)

**FAQs:**

- *What is the difference between a Helm repository and an OCI Helm repository?*
- *How do I configure a private Helm chart repository in Argo CD?*
- *Why should I use OCI Helm repositories instead of traditional Helm repositories?*

