---
title: "Prepare your Helm chart"
description: "Package your application as a chart ARX can install: where ARX reads it from, the labels and persistence values the platform bindings rely on, and how to check the chart against your arx.yaml"
canonical_url: "https://developer.ardge.com/en/guide/isv/prepare-helm-chart"
---
# Prepare your Helm chart

> Package your application as a chart ARX can install: where ARX reads it from, the labels and persistence values the platform bindings rely on, and how to check the chart against your arx.yaml

## Overview

The chart is what actually runs on the system. ARX renders it at install time, applies the values file that ships with your template, and the operator wires the platform resources your `arx.yaml` declares into the pods it creates.

This page covers what the chart has to expose for those bindings to land, and how to check them before you publish.

## What you'll learn

- Where ARX reads your chart from, and which values file it applies
- Label your pods so the platform can target them
- Expose the persistence values a storage binding writes into
- Check the chart against your `arx.yaml`

## Applies to

- ARX OS 1.0 and later

## Before you begin

- Your application is packaged as a container image, and that image can be pulled from an address the ARX system can reach
- You can publish a chart to an OCI registry, or to an http/https chart repository, that the ARX system can reach
- You know which platform resources your application needs — see [Prepare your arx.yaml](https://developer.ardge.com/en/guide/isv/prepare-arx-yaml)

<note>

**Download the skills:** the bundle is [arx-isv-skills-v0.6.4.zip](https://developer.ardge.com/skills/arx-isv-skills-v0.6.4.zip) (about 185 KB) — unzip it and copy the folders inside into your project's `.claude/skills/`.

</note>

## Where ARX reads your chart

The `deployment` block of your app template names the repository, the chart, and the values files applied on top of it. Two repository types are understood — `oci` for an OCI registry, `default` for an http/https chart repository:

```yaml
deployment:
  type: helm
  helmRepository:
    type: oci
    name: your-registry
    url: oci://registry.example.com/charts
  helmChart:
    chart: your-app
    version: "2.0.1"
    valuesFiles:
      - manifests/values.yaml
```

`valuesFiles` lists paths relative to the template directory, conventionally `manifests/values.yaml`. Mirror the chart's own `values.yaml` there and override only what this deployment needs, so the chart stays generic and the ARX-specific settings sit with the template.

<warning>

**Two versions, two things:** `metadata.version` is the application version the marketplace shows and judges upgrades against; `helmChart.version` is the version of the chart that deploys it. Nothing checks the two against each other.

</warning>

## Label your pods

The platform targets pods by their `app.kubernetes.io/component` label. It is the label `components` filters on in an `arx.yaml` block, and the one a network service matches through `selectorLabelValue`.

Set it on every workload in the chart. If your chart uses a different label key, declare `selectorLabel` in the network service so the platform matches on yours instead — that makes `selectorLabelValue` required.

## Serve the ports you declare

Each service in the `network` block names the ports it serves. Your pods have to listen on exactly those ports, and carry the label the service selects on.

Pinned `nodePort` values are allocated cluster-wide, so a value another application in the same repository already pins is guaranteed to fail for whichever installs second. Leave `nodePort` out for dynamic allocation unless you need a fixed one.

## Expose your persistence values

A storage slot in `arx.yaml` reaches into the chart in two different ways, and getting them right is what makes the PVC bind:

- **nameTemplate has to render the PVC name your chart actually creates.** That is how the operator finds the PVC later. It is plain Go `text/template`, not Helm's dialect — Sprig is not available, so a chart's `fullname` helper cannot be transcribed. Work out what it renders to for the release, and pin the chart's name override in `manifests/values.yaml` so the result is a name you can state
- **storageClass, accessMode, and the optional subPath and label are dotted paths into your chart's values.yaml**, not values. The chart has to expose them, and the paths have to resolve. Set `accessModeFormat: list` when the chart expects a list rather than a scalar

## Check the chart against your arx.yaml

The template validator has a chart check: it pulls each app's chart and verifies that the value paths its `arx.yaml` declares resolve in the chart's `values.yaml`, and that a slot declaring `accessModeFormat: string` points at a scalar.

Run it for every application that declares `storage` — a path that does not resolve means the operator silently fails to wire the PVC at install time, with nothing failing loudly at install. The check does not cover `nameTemplate`, which renders an object name rather than a values path, so that one stays yours to verify.

## Next step

<card-group>
<card icon="i-lucide-upload" title="Publish to App Market for testing" to="https://developer.ardge.com/en/guide/isv/publish-to-app-market">

Import the template into an ARX system, install your application, and test it on real hardware

</card>
</card-group>


## Sitemap

See the full [sitemap](https://developer.ardge.com/sitemap.md) for all pages.
