Skip to main content

How to Store Helm Charts in Azure Container Registry

Helm has a lot of awesome tools around it, but chart storage has always been an issue. Let's see how we can make the process a lot easier!

LHB Community

Warp Terminal

One of the great advantages of understanding how the container ecosystem works is that you can use the same pattern for several specifications.

It has not been long since, Helm announced that it would be supporting OCI Artifacts, which are nothing more than an OCI open specification for distributing container images and other types of data, called artifacts.

This specification, like all other OCI specifications, is cloud-agnostic, which makes it a fantastic tool to work with.

Container Records

A container record (CR), or a registry, is something that everyone who has ever had to be involved with containers has had to use. The CR is where we store our container images, so we can fetch them from anywhere and whenever we want.

In essence, an image is basically a set of files that follows a structure more or less like this:

 ├── blobs
       │   └── sha256
       │       ├── 1b251d38cfe948dfc0a5745b7af5ca574ecb61e52aed10b19039db3...
       │       ├── 31fb454efb3c69fafe53672598006790122269a1b3b458607dbe106...
       │       └── 8ec7c0f2f6860037c19b54c3cfbab48d9b4b21b485a93d87b64690f...
       ├── index.json
       └── oci-layout

The file index.json is the list of all the manifests available, that is, it is the list of all the images available in a location. In our case, it is a list of all the helm charts.

Each file inside blobs/sha256is a JSON that identifies an artifact, be it an image or a chart. This JSON is compliant with the OCI specification for SHA files.

In short, they are a list of settings describing the characteristics of the blob, its settings, properties, file system layers, and also the initial commands.

In the case of a Helm Chart, we have the following file:

  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.cncf.helm.config.v1+json",
    "digest": "sha256:8ec7c0f2f6860037c19b54c3cfbab48d9b4b21b485a93d87b64690fdb68c2111",
    "size": 117
  "layers": [
      "mediaType": "application/tar+gzip",
      "digest": "sha256:1b251d38cfe948dfc0a5745b7af5ca574ecb61e52aed10b19039db39af6e1617",
      "size": 2487

Notice that we have a differentiation of mediaType, while a common Docker image has a type application/vnd.oci.image.config.v1+json.

Here, we have a type application/vnd.cncf.helm.config, the same goes for layers, each layer of an OCI image is of type application/vnd.oci.image.layer.v1.tar+gzip, while here we only have the format .tar.gz.

Hosting charts in Azure Container Registry

Hosting Helm charts on Azure CR is pretty much similar to storing them locally. You need to have access to azure via Azure CLI. I'll assume you already have the Azure CLI, so let's create our ACR.

First we have to create our resource group and then the ACR with the commands:

az group create -n helm-reg -l eastus
az acr create -n chartregistry$RANDOM -g helm-reg --sku Basic -o tsv --query loginServer

A tip is to store the repository name in a variable:

export ACR=$(az acr create -n chartregistry$RANDOM -g helm-reg --sku Basic -o tsv --query loginServer)

Now we are going to log in to our registry using Azure managed keys, but we need to enable administrative control with az acr update -n $ACR --admin-enabled true.

Next, run the following two commands to fetch the login credentials and save them in shell:

export ACRUSER=$(az acr credential show -n $ACR --query username -o tsv)
export ACRPASS=$(az acr credential show -n $ACR --query 'passwords[0].value' -o tsv)

Now we can log into our registry with Helm using helm registry login $ACR --username $ACRUSER --password $ACRPASS, and from here we already have our registry configured.

Let's create another artifact with helm chart save hrepo $ACR/hrepo:2.1.3 (as an example, I'll use a chart from a dumpy repository named hrepo). Then we'll push it with helm chart push $ACR/hrepo:3.8.0.

Once it's there, we'll be able to list everything in the repository with an Azure CLI command:

az acr repository show -n $ACR --repository hrepo

Notice that we are going to have an output that is exactly what we sent:

  "changeableAttributes": {
    "deleteEnabled": true,
    "listEnabled": true,
    "readEnabled": true,
    "writeEnabled": true
  "createdTime": "2022-03-05T20:56:49.6118202Z",
  "imageName": "hrepo",
  "lastUpdateTime": "2022-03-05T20:56:49.7812323Z",
  "manifestCount": 1,
  "registry": "",
  "tagCount": 1

We can also get more details with the command show-manifestsby adding a --detail:

az acr repository show-manifests -n $ACR --repository hrepo --detail

This will give us exactly the definition of an OCI artifacts:

    "changeableAttributes": {
      "deleteEnabled": true,
      "listEnabled": true,
      "quarantineState": "Passed",
      "readEnabled": true,
      "writeEnabled": true
    "configMediaType": "application/vnd.cncf.helm.config.v1+json",
    "createdTime": "2022-03-05T20:56:49.7213057Z",
    "digest": "sha256:4780713fa23d7144d356c353795b5b84e66ad2b8bbd47c7118b4b85435d50bbc",
    "imageSize": 1378,
    "lastUpdateTime": "2022-03-05T20:56:49.7213057Z",
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "tags": [

To store it, we have to simply:

helm chart pull $ACR/hrepo:3.8.0
helm chart export $ACR/hrepo:3.8.0 -d ./destination
helm install hrepo-acr ./destination


Although using Helm is easy, yet there isn't a "simple" way to host a Helm chart as some kind of private record.

While Helm has excellent tools like the Chart Museum, they are still not completely standard and for easy distributed development it is essential that we have open standards that everyone can as a whole can follow.

Helm has recently moved OCI Registry support from experimental to general feature, which is a strong indication that the container ecosystem is becoming increasingly better.

Author: Talha Khalid is a freelance web developer and technical writer.
LHB Community