k8wiz
All graphs

Helm graph

OCI artifacts: Helm charts, Flux artifacts and image volumes

Three kinds of content in one registry, each a manifest with its own media types: a Helm chart pulled by helm, a Flux artifact pulled by an OCIRepository, and a data image mounted into a Pod as an image volume.

A registry doesn't care whether a manifest describes a container. It stores a config blob and layer blobs, and the media types say what they are. Helm, Flux and many other tools use that to ship their own formats through the registry you already run, with the same authentication, replication and digests.

Each client reads the manifest, picks the layer whose media type it understands and downloads only that blob. The third example is a plain image used as data: Kubernetes 1.36 can mount it as a read-only volume.

Interactive graph

Click a resource to see what it does, or follow the setup step by step.

Outside the clusterMade by a toolYou write itComes with an add-ontrafficreferences

How it works

All 12 resources and what they do

Client helm CLI
Runs helm install shop oci://ghcr.io/acme/charts/shop --version 0.9.0. It resolves the tag 0.9.0, reads the manifest and downloads the chart layer. No index.yaml is involved.
Image manifest charts/shop:0.9.0
Written by helm push. The config media type application/vnd.cncf.helm.config.v1+json is how registries and tools recognize it as a chart.
Config Chart.yaml as JSON
The chart's metadata: name, version, appVersion, description. Tools can show it without downloading the chart itself.
Layer shop-0.9.0.tgz
The packaged chart, byte for byte the .tgz that helm package made, with the media type application/vnd.cncf.helm.chart.content.v1.tar+gzip. A signed chart has a second layer, application/vnd.cncf.helm.chart.provenance.v1.prov.
Kustomization shopkustomize.toolkit.fluxcd.io/v1
Applies the manifests from the OCIRepository's artifact. The cluster never talks to Git. CI pushed the manifests to the registry instead.
OCIRepository shop-manifestssource.toolkit.fluxcd.io/v1
source-controller checks the latest tag every 5 minutes. When its digest changes, it downloads the layer that layerSelector names and, with operation: extract, unpacks it as the new artifact.
Image manifest manifests/shop:4f2c8e1
Written by flux push artifact in CI, then tagged latest with flux tag artifact. The annotations record the Git source and revision, which Flux shows as the artifact's origin.
Layer deploy/ as tar.gz
The ./deploy directory from the app repo, packed by flux push artifact --path=./deploy. source-controller unpacks it and serves it to the Kustomization.
Pod inferencev1
Runs the inference server from one image and mounts the model from another with an image volume. Updating the model means pushing a new model image, not rebuilding the server.
Container runtime containerd 2.1+
Pulls the volume image like any image, unpacks its layers into a snapshot and mounts it read-only into the Pod. Image volumes need containerd 2.1 or CRI-O 1.31 or later. CRI-O can also mount artifacts that aren't images.
Image manifest models/sentiment:2026-10
An ordinary image manifest, built FROM scratch with a single COPY of the model files. It has no entrypoint and never runs. The kubelet only needs its filesystem.
Layer model files, 256 MB
The model weights and tokenizer, compressed with zstd. Nodes that already have this digest skip the download, so rolling out a new server version doesn't move the model again.

Common mistakes

  • Some registries reject manifests with media types they don't know. All the major ones accept artifacts now, but older self-hosted registries and some proxies may need configuration.
  • Helm stores version 1.2.3+build.4 under the tag 1.2.3_build.4, because tags can't contain +. Tools that compute the tag themselves have to do the same.
  • An OCIRepository without layerSelector takes the first layer. That works for Flux artifacts, but for a chart with a provenance layer, select the chart's media type explicitly.
  • Image volumes are read-only, and files keep the owner and mode they have in the image. Set ownership and permissions when you build the image, so the container's user can read them.
  • A moving tag like latest in an image volume with pullPolicy: IfNotPresent keeps the old content on nodes that already pulled it. Use dated or versioned tags, or a digest.

The manifests you write

Everything above that is marked "You write it", in one file. The rest comes from add-ons and controllers.

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: shop
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: OCIRepository
    name: shop-manifests
  path: ./
  prune: true
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: shop-manifests
  namespace: flux-system
spec:
  interval: 5m
  url: oci://ghcr.io/acme/manifests/shop
  ref:
    tag: latest
  layerSelector:
    mediaType: application/vnd.cncf.flux.content.v1.tar+gzip
    operation: extract
---
apiVersion: v1
kind: Pod
metadata:
  name: inference
  namespace: ml
spec:
  containers:
    - name: server
      image: ghcr.io/acme/inference-server:3.2.0
      volumeMounts:
        - name: model
          mountPath: /models/sentiment
          readOnly: true
  volumes:
    - name: model
      image:
        reference: ghcr.io/acme/models/sentiment:2026-10
        pullPolicy: IfNotPresent

Keep going

More graphs