Skip to main content

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

Deploying Cert-Manager from a Mirrored Private OCI Registry (Harbor)
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) restrict pulling images only from specific Helm chart repositories.

Starting April 1, 2025, Docker will introduce new pull rate limits:

  • 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 Helm 3.8.0, 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.

Argo CD GUI: + CONNECT REPO
Fig. 1: Argo CD GUI: + CONNECT REPO
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:

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?

  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:

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) 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.
Deploying a Mirrored Cert-Manager Helm Chart via Docker Private OCI 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 and create a Personal Access Token (PAT).

Docker GUI: Create PAT 
Fig. 5: Docker GUI: Create PAT 

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

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:

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:

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:

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:

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

or by running:

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.

Adding a Docker Private OCI Helm Repository to Argo CD
Fig. 7: Adding a Docker Private OCI Helm Repository to Argo CD
After successfull added 
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:

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 or 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.

 Deploying Cert-Manager via Docker Private OCI Helm Repository
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.

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

Application YAML Example

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

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 with a Cluster Generator:


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, here’s how you define it:

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.
Deploying a Mirrored Cert-Manager Helm Chart via GitLab Private OCI 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: 

GitLab GUI: Create Project Access Token
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:

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

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

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::

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:

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.

Adding a GitLab Private OCI Helm Repository to Argo CD
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:

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: registry.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.

Deploying Cert-Manager via GitLab Private OCI Helm Repository
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:

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.
Deploying a Mirrored Cert-Manager Helm Chart via Harbor Private OCI 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
Create Robot Account (Part 2)
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:

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:

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::

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:

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-domain.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.

Connecting a Harbor Private OCI Helm Repository to Argo CD
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:

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:

Deploying an Application via Harbor Private OCI Helm Repository in Argo CD
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:

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:

Engineering Support:

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

Visit our other blogs!

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?