Files
helmfile/docs/configuration.md
T
c36dbfd417 fix: resolve OCI version constraints before deriving the shared chart cache path (#2768)
* fix: resolve OCI version constraints before deriving the shared chart cache path

When an OCI release uses a semver constraint (e.g. `~1`, `^2.0.0`, `*`),
`getOCIChartPath` currently derives the on-disk cache directory from the
raw constraint string via `safeVersionPath`, which substitutes constraint
characters (`~`, `^`, `>`, `<`, `!`, `|`, `=`, ` `, `,`, `*`) with `_`.
So `version: ~1` becomes `.../mychart/_1/` on disk. `acquireChartLock`
then refuses to refresh anything under the shared cache dir to avoid
race conditions between concurrent processes, so once the constraint is
first resolved and written to `_1/`, every subsequent render on that
machine (or that container replica) returns the pinned tarball
regardless of newer matching tags being published.

In multi-pod deployments like ArgoCD's argocd-repo-server this shows up
as intermittent stale renders: different pods populate their caches at
different moments and serve different snapshots of the same `~1`
release forever.

Fix: for OCI releases whose `version` looks like a constraint, run
`helm show chart <ref> --version <constraint> [flags]` and use the
returned metadata.Version as the effective version for all downstream
cache-key and path derivation. Helm already resolves the constraint
against the registry and returns the concrete matching Chart.yaml.
Callers get a content-addressable cache path (`.../mychart/1.0.1/`)
that naturally invalidates when the constraint resolves to a new
version. Exact-version releases and non-OCI releases skip the extra
call.

Adds an opt-out `resolveOCIVersions` field on `helmDefaults` and
`ReleaseSpec` (both default true). If the resolution call fails
transiently, the resolver logs a warning and falls back to the
pre-fix behavior so a network hiccup doesn't break rendering.

Adds `ShowChartWithFlags` to helmexec.Interface so the existing
`ShowChart` API stays backwards compatible.

Resolves #2766

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* refactor: move ShowChartWithFlags to a ChartInspector capability interface

Address Copilot review feedback on PR #2768: adding a method to the
exported helmexec.Interface is a source-breaking change for every
third-party implementation and mock of that interface, even though
ShowChart itself stayed backward-compatible.

Move ShowChartWithFlags off Interface and onto a new capability
interface, helmexec.ChartInspector, following the same pattern used
by the existing DependencyUpdater capability interface. The concrete
execer and the exectest.Helm test stub still satisfy it (they
already have the method); the OCI resolver in state.HelmState now
type-asserts and falls back to the pre-fix caching behavior when the
capability is absent, so downstream callers with their own
helmexec.Interface implementations keep compiling untouched.

Adds TestResolveOCIConstraintVersion_ChartInspectorFallback that
exercises the type-assertion path with a helm value that satisfies
Interface but deliberately does not satisfy ChartInspector.

Reverts ShowChartWithFlags additions from testutil.noCallHelmExec
and app_test.mockHelmExec since Interface no longer requires them.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* fix: detect wildcard-segment semver constraints (1.x, 1.X) as constraints

Address Copilot review feedback on PR #2768: the previous
isVersionConstraint implementation scanned the input for operator
characters (~, ^, >, <, !, |, =, space, comma, *). Masterminds/semver
also accepts wildcard-segment constraints like "1.x", "1.X", "1.x.x",
and "1.2.X" that contain no operator characters. Those would slip past
the classifier, bypass OCI constraint resolution, and remain cached
forever under the raw ".../mychart/1.x/" path — the same stale-cache
bug the PR is meant to fix.

Replace the character scan with a semver-parser-based check: a value
is a constraint iff Masterminds/semver rejects it as a NewVersion but
accepts it as a NewConstraint. This correctly:

  - Recognizes wildcard forms (1.x, 1.X, 1.x.x, 1.2.x, v1.x).
  - Preserves exact versions where "x" appears in prerelease metadata
    ("1.0.0-alpha.x") or build metadata ("1.0.0+x", "1.0.0+build.x.1")
    without misclassifying them, which a naive "add x to the scanned
    charset" fix would have gotten wrong.
  - Continues to classify values that are neither a version nor a
    constraint (empty string, "latest", junk) as non-constraints; helm
    handles those elsewhere.

Removes the now-unused versionConstraintChars string constant.

Expands TestIsVersionConstraint with 8 wildcard cases and 3 prerelease
/build metadata cases containing "x", plus 2 non-parseable inputs.
Adds a "wildcard segment constraint resolves to concrete version"
subtest to TestResolveOCIConstraintVersion so the end-to-end pipeline
is exercised for a version string that has no operator characters.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* test: add getOCIChart integration test proving cache-path/pull-flag wiring

Address Copilot review feedback on PR #2768. The existing unit test
exercised resolveOCIConstraintVersion in isolation but did not prove
that its output was propagated into the downstream cache key, cache
path, and `helm chart pull --version` flag. Add a targeted
integration test that:

  1. Calls getOCIChart with a constraint release (`~1`) and a helm
     mock whose ShowChartWithFlags returns Chart.yaml version 1.0.1.
  2. Asserts helm chart pull receives `--version 1.0.1`, not `~1`.
  3. Asserts the destination path passed to helm chart pull contains
     the resolved-version segment (`/1.0.1/`) and does NOT contain
     the raw-constraint segment (`/_1/`).
  4. Reads back the on-disk Chart.yaml under the cache path to
     confirm resolved version, path, and flag agree end to end.

Add a second test that runs the same release twice with different
resolver outputs (1.0.1, then 1.0.2 — simulating a newly published
matching tag) and asserts the two resolutions land in distinct cache
directories. This is the promise of the fix: once the raw constraint
is out of the path, a new matching tag stops silently reusing the
previously-resolved cache entry.

The integration test flushed out a real correctness gap in the
initial fix: getOCIChart resolved release.Version and chartVersion
but did NOT recompute the qualified OCI ref that
getOCIQualifiedChartName built pre-resolution. Helm was therefore
receiving `oci://<repo>/<chart>:<constraint>` alongside a
`--version <resolved>` flag — at best redundant, at worst rejected
by future Helm versions. Fixed by re-invoking
getOCIQualifiedChartName on the mutated release copy so the
embedded tag also carries the resolved value.

Isolates the shared helmfile cache via `t.Setenv(HELMFILE_CACHE_HOME,
t.TempDir())` so the OutputDirTemplate == "" code path (which writes
into remote.CacheDir) does not touch the user's real
`~/.cache/helmfile` during test runs.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* refactor: flatten OCI constraint-resolution wiring in getOCIChart

Address review feedback on PR #2768:

- Extract the inline resolve/requalify block from getOCIChart into
  applyOCIConstraintResolution, keeping getOCIChart flat (guard-clause
  style) and making the resolution wiring independently testable. The
  helper returns the (possibly updated) release, qualified chart name,
  and chart version; every failure mode returns its inputs unchanged.

- On a re-qualify failure after a successful resolution, fall back to
  the pre-fix behavior entirely (raw constraint in cache key, ref, AND
  --version flag) instead of the previous half-resolved mix (resolved
  version in the cache key, raw constraint in the path and flag), which
  could desynchronize the in-process cache key from the on-disk path.

- Build the 'helm show chart' ref by reusing parseOCIChartRef instead of
  re-implementing its last-slash/last-colon tag-splitting inline. Same
  behavior for all realistic refs (registry ports preserved), and it
  also handles the digest suffix should one ever reach this point.

- Drop --devel from the resolver flags: helm documents --devel as
  ignored whenever --version is set, and --version is always passed on
  this path.

No behavior change intended beyond the requalify-failure fallback
(which cannot realistically trigger) and the removal of the inert
--devel flag.

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: classify partial semver versions (1, 1.2) as OCI constraints

Address review feedback on PR #2768: Masterminds' lenient parser accepts
partial versions like "1" or "1.2" as versions, so the previous
classifier (NewVersion fails && NewConstraint succeeds) treated them as
exact pins. But helm's OCI resolution — registry.GetTagMatchingVersionOrConstraint
— honors a version string as an exact pin ONLY when a registry tag
literally equals it; otherwise it parses the string as a constraint, and
"1"/"1.2" float across 1.x.y/1.2.y tags. Caching those under their raw
spelling reproduces the stale-cache bug of issue #2766, just with a
narrower trigger.

Replace the NewVersion probe with isFullSemver, which additionally
requires the whole major.minor.patch triple to be spelled out
(optional v prefix, prerelease, and build metadata all still count as
exact when the core is fully qualified). When a registry does carry a
literal tag equal to the version string, the resolver's
metadata.Version == chartVersion path reports no change, so literal-tag
pins keep today's behavior.

TestIsVersionConstraint: "1"/"1.0" flip to constraints, joined by new
v1.2/0/v1 cases and a 1.2.3 exact case. TestResolveOCIConstraintVersion
gains a "partial version resolves" subtest.

Docs updated to describe the parser-based classification instead of
"constraint characters".

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: skip OCI constraint resolution under skipRefresh

Address review feedback on PR #2768: the resolver ran even under
--skip-refresh, so offline and cache-only workflows gained a
'helm show chart' registry attempt per constraint-versioned OCI release.
It degraded gracefully (warn + fallback), but added registry-timeout
latency and warning noise per release.

skipOCIConstraintResolution now suppresses resolution when any of the
skipRefresh levels is set — CLI --skip-refresh (forced), per-release
skipRefresh, or helmDefaults.skipRefresh — with the same precedence the
other skipRefresh consumers in prepareChartForRelease use. Skipped runs
fall back to the constraint-keyed cache path, i.e. they reuse whatever a
previous non-skipped run resolved, which is what 'skip checking for
updates to cached charts' means for constraint versions.

The existing issue #2766 integration tests flip their opts to
SkipRefresh: false since they assert resolution happens. New coverage:
TestSkipOCIConstraintResolution (tri-state precedence table) and
TestGetOCIChart_SkipRefreshSkipsConstraintResolution (no inspector call,
raw constraint in --version and cache path).

Signed-off-by: yxxhero <aiopsclub@163.com>

* perf: memoize OCI constraint resolution per chart+constraint

Address review feedback on PR #2768: resolution ran before the
in-process chart-cache fast path and was not memoized, so every
constraint-versioned OCI release paid its own 'helm show chart' registry
round-trip on every render — including N releases sharing the same
chart+constraint, whose parallel workers could even resolve to different
versions if the registry changed between their lookups.

Memoize successful resolutions in resolvedOCIConstraints keyed by
(chart ref, constraint), mirroring the downloadedCharts pattern:

- Releases sharing a chart+constraint cost one round-trip per process
  and consistently use one resolved version per run.
- Only successful resolutions are memoized; failures may be transient.
- Flags are not part of the key: they govern TLS/verification/registry
  credentials, not which tag a constraint matches (--devel is already
  omitted as it is ignored whenever --version is set).
- Concurrent misses may both hit the registry; last write wins,
  harmlessly.

resetResolvedOCIConstraintsForTest is added alongside the existing
resetChartCacheForTest and wired into the issue #2766 tests — notably
ResolvesToDifferentVersionsPicksSeparateCachePaths, which reuses the
same chart+constraint across its two runs and would otherwise be served
the first resolution from the memo (which is exactly the intended
per-process semantics).

New coverage: TestResolveOCIConstraintVersion_Memoized (memo hit skips
the registry, different constraint is a different key) and
TestGetOCIChart_SharedConstraintResolvedOncePerProcess (two releases,
one inspector call, one pull, same path).

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: cover URL-embedded OCI constraint resolution

Address review feedback on PR #2768: the existing integration tests only
exercised the repo-aliased spelling (chart: myrepo/mychart, version:
'~1') and the version-field spelling. The chart-URL spelling
(chart: oci://<registry>/<chart>:~1) takes a different branch in
getOCIQualifiedChartName — the URL version is deliberately NOT embedded
into the qualified ref and flows through --version only — so its
re-qualification after constraint resolution (release.Version mutated to
the resolved value, versionInURL still the constraint) was untested.

TestGetOCIChart_URLEmbeddedConstraintResolves asserts the resolver
receives the URL-embedded constraint, helm chart pull receives the
resolved version via --version with a tag-less ref, and the cache path
carries the resolved version segment instead of the raw constraint.

Also gofmt-aligns the test tables added in earlier commits and drops a
redundant 1.2.3 test case that tripped goconst.

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: note empty-version OCI releases are unaffected by resolveOCIVersions

Releases with no version: at all keep their pre-existing semantics: helm
picks the latest tag at pull time and helmfile caches it under a
version-less shared-cache path. Document the limitation alongside the
other resolveOCIVersions scope notes.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Samuel Archambault <samuel.archambault@getmaintainx.com>
Co-authored-by: yxxhero <aiopsclub@163.com>
2026-09-07 16:50:25 +08:00

30 KiB

Configuration Reference

This page is a comprehensive reference for all options available in helmfile.yaml.

If you're new to Helmfile, start with the Getting Started tutorial on the home page, then read Writing Helmfile for patterns. Come back here when you need to look up a specific field.

CAUTION: This documentation is for the development version of Helmfile. If you are looking for the documentation for any of releases, please switch to the corresponding release tag like v0.143.4.

Quick Reference

A helmfile.yaml has these top-level sections:

Section Purpose
repositories Helm chart repositories to use
releases The Helm releases to deploy (the core of helmfile)
helmDefaults Default Helm options for all releases
environments Environment-specific values (dev, staging, prod)
helmfiles Include other helmfile.yaml files (nesting)
bases Shared base files merged before this helmfile
values Default values available in templates
commonLabels Labels applied to all releases
templates Reusable release templates
defaultInherit Default template(s) for all releases to inherit
hooks Global lifecycle hooks
apiVersions / kubeVersion Kubernetes version capabilities
llm OpenAI-compatible LLM config for helmfile doctor (optional)

Full Reference

The default name for a helmfile is helmfile.yaml:

# Chart repositories used from within this state file
#
# Use `helm-s3` and `helm-git` and whatever Helm Downloader plugins
# to use repositories other than the official repository or one backend by chartmuseum.
repositories:
# To use official "stable" charts a.k.a https://github.com/helm/charts/tree/master/stable
- name: stable
  url: https://charts.helm.sh/stable
# To use official "incubator" charts a.k.a https://github.com/helm/charts/tree/master/incubator
- name: incubator
  url: https://charts.helm.sh/incubator
# helm-git powered repository: You can treat any Git repository as a charts repository
- name: polaris
  url: git+https://github.com/reactiveops/polaris@deploy/helm?ref=master
# Advanced configuration: You can setup basic or tls auth and optionally enable helm OCI integration
- name: roboll
  url: roboll.io/charts
  certFile: optional_client_cert
  keyFile: optional_client_key
  # username is retrieved from the environment with the format <registryNameUpperCase>_USERNAME for CI usage, here ROBOLL_USERNAME
  username: optional_username
  # password is retrieved from the environment with the format <registryNameUpperCase>_PASSWORD for CI usage, here ROBOLL_PASSWORD
  password: optional_password
  oci: true
  passCredentials: true
  verify: true
  keyring: path/to/keyring.gpg
# Advanced configuration: You can use a ca bundle to use an https repo
# with a self-signed certificate
- name: insecure
  url: https://charts.my-insecure-domain.com
  caFile: optional_ca_crt
# Advanced configuration: You can skip the verification of TLS for an https repo
- name: skipTLS
  url: https://ss.my-insecure-domain.com
  skipTLSVerify: true
# Advanced configuration: Connect to a repo served over plain http
- name: plainHTTP
  url: http://just.http.domain.com
  plainHttp: true

# context: kube-context # this directive is deprecated, please consider using helmDefaults.kubeContext

# Path to alternative helm binary (--helm-binary)
# Supports both Helm 3.x and Helm 4.x
helmBinary: path/to/helm

# Path to alternative kustomize binary (--kustomize-binary)
kustomizeBinary: path/to/kustomize

# Path to alternative lock file. The default is <state file name>.lock, i.e for helmfile.yaml it's helmfile.lock.
lockFilePath: path/to/lock.file

# Default values to set for args along with dedicated keys that can be set by contributors, cli args take precedence over these.
# In other words, unset values results in no flags passed to helm.
# See the helm usage (helm SUBCOMMAND -h) for more info on default values when those flags aren't provided.
helmDefaults:
  kubeContext: kube-context          #dedicated default key for kube-context (--kube-context)
  cleanupOnFail: false               #dedicated default key for helm flag --cleanup-on-fail
  # additional and global args passed to helm (default "")
  args:
    - "--set k=v"
  diffArgs:
    - "--suppress-secrets"
  syncArgs:
    - "--labels=app.kubernetes.io/managed-by=helmfile"
  # extra args appended to the helm template / helm diff rendering (default "").
  # most commonly "--dry-run=server" to enable the helm lookup() function.
  # overridden by the --template-args CLI flag on a per-invocation basis.
  templateArgs:
    - "--dry-run=server"
  # verify the chart before upgrading (only works with packaged charts not directories) (default false)
  verify: true
  keyring: path/to/keyring.gpg
  #  --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint' (default false)
  skipSchemaValidation: false
  # wait for k8s resources via --wait. (default false)
  wait: true
  # DEPRECATED: waitRetries is no longer supported as the --wait-retries flag was removed from Helm.
  # This configuration is ignored and preserved only for backward compatibility.
  # waitRetries: 3
  # if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout (default false)
  waitForJobs: true
  # time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
  timeout: 600
  # performs pods restart for the resource if applicable (default false)
  recreatePods: true
  # forces resource update through delete/recreate if needed (default false)
  force: false
  # limit the maximum number of revisions saved per release. Use 0 for no limit. (default 10)
  historyMax: 10
  # automatically create release namespaces if they do not exist (default true)
  createNamespace: true
  # if used with charts museum allows to pull unstable charts for deployment, for example: if 1.2.3 and 1.2.4-dev versions exist and set to true, 1.2.4-dev will be pulled (default false)
  devel: true
  # When set to `true`, skips running `helm dep up` and `helm dep build` on this release's chart.
  # Useful when the chart is broken, like seen in https://github.com/roboll/helmfile/issues/1547
  skipDeps: false
  # When set to `true` (default), resolves an OCI chart's semver constraint
  # (e.g. `~1`, `^2.0.0`, `*`, `1.x`, or any version that is not a
  # fully-qualified `X.Y.Z` semver — including partial versions like `1` or
  # `1.2`, which helm resolves as floating ranges) to a concrete registry tag
  # before deriving the on-disk cache path under `$XDG_CACHE_HOME/helmfile`.
  # This keeps the cache content-addressable so a newer matching tag is picked
  # up on the next run instead of the previously-resolved (now stale) version.
  # Set to `false` to preserve the pre-fix behavior of caching under the raw
  # constraint string. Resolution is also skipped when skipRefresh is enabled
  # (CLI `--skip-refresh`, per-release, or here): helmfile then reuses whatever
  # a previous run resolved. Releases with no `version:` at all are also
  # unaffected: helm picks the latest tag at pull time and helmfile caches it
  # under a version-less path (same as before this setting existed). Exact
  # `X.Y.Z` versions (e.g. `1.0.1`) and non-OCI releases are unaffected. See
  # issue #2766.
  resolveOCIVersions: true
  # If set to true, reuses the last release's values and merges them with ones provided in helmfile.
  # This attribute, can be overriden in CLI with --reset/reuse-values flag of apply/sync/diff subcommands
  reuseValues: false
  # propagate `--post-renderer` to helmv3 template and helm install
  postRenderer: "path/to/postRenderer"
  # propagate `--post-renderer-args` to helmv3 template and helm install. This allows using Powershell
  # scripts on Windows as a post renderer
  postRendererArgs:
  - PowerShell
  - "-Command"
  - "theScript.ps1"
  #	cascade `--cascade` to helmv3 delete, available values: background, foreground, or orphan, default: background
  cascade: "background"
  # insecureSkipTLSVerify is true if the TLS verification should be skipped when fetching remote chart
  insecureSkipTLSVerify: false
  # plainHttp is true if fetching the remote chart should be done using HTTP
  plainHttp: false
  # --wait flag for destroy/delete, if set to true, will wait until all resources are deleted before mark delete command as successful
  deleteWait: false
  # Timeout is the time in seconds to wait for helmfile destroy/delete (default 300)
  deleteTimeout: 300
  # suppressOutputLineRegex is a list of regex patterns to suppress output lines from helm diff (default []), available in helmfile v0.162.0
  suppressOutputLineRegex:
    - "version"
  # syncReleaseLabels is a list of labels to be added to the release when syncing.
  syncReleaseLabels: false


# these labels will be applied to all releases in a Helmfile. Useful in templating if you have a helmfile per environment or customer and don't want to copy the same label to each release
commonLabels:
  hello: world

# The desired states of Helm releases.
#
# Helmfile runs various helm commands to converge the current state in the live cluster to the desired state defined here.
releases:
  # Published chart example
  - name: vault                            # name of this release
    namespace: vault                       # target namespace
    createNamespace: true                  # automatically create release namespace (default true)
    labels:                                # Arbitrary key value pairs for filtering releases
      foo: bar
    chart: roboll/vault-secret-manager     # the chart being installed to create this release, referenced by `repository/chart` syntax
    version: ~1.24.1                       # the semver of the chart. range constraint is supported
    condition: vault.enabled               # Filters releases by the boolean value at `vault.enabled`. Can also be set directly to true or false.
    missingFileHandler: Warn # set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues.
    missingFileHandlerConfig:
      # Ignores missing git branch error so that the Debug/Info/Warn handler can treat a missing branch as non-error.
      # See https://github.com/helmfile/helmfile/issues/392
      ignoreMissingGitBranch: true
    # Values files used for rendering the chart
    values:
      # Value files passed via --values
      - vault.yaml
      # Inline values, passed via a temporary values file and --values, so that it doesn't suffer from type issues like --set
      - address: https://vault.example.com
      # Go template available in inline values and values files.
      - image:
          # The end result is more or less YAML. So do `quote` to prevent number-like strings from accidentally parsed into numbers!
          # See https://github.com/roboll/helmfile/issues/608
          tag: {{ requiredEnv "IMAGE_TAG" | quote }}
          # Otherwise:
          #   tag: "{{ requiredEnv "IMAGE_TAG" }}"
          #   tag: !!string {{ requiredEnv "IMAGE_TAG" }}
        db:
          username: {{ requiredEnv "DB_USERNAME" }}
          # value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1'
          password: {{ requiredEnv "DB_PASSWORD" }}
        proxy:
          # Interpolate environment variable with a fixed string
          domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
          scheme: {{ env "SCHEME" | default "https" }}
    # Use `values` whenever possible!
    # `setString` translates to helm's `--set-string key=val`
    setString:
    # set a single array value in an array, translates to --set-string bar[0]={1,2}
    - name: bar[0]
      values:
      - 1
      - 2
    # set a templated value
    - name: namespace
      value: {{ .Namespace }}
    # `set` translates to helm's `--set key=val`, that is known to suffer from type issues like https://github.com/roboll/helmfile/issues/608
    set:
    # single value loaded from a local file, translates to --set-file foo.config=path/to/file
    - name: foo.config
      file: path/to/file
    # set a single array value in an array, translates to --set bar[0]={1,2}
    - name: bar[0]
      values:
      - 1
      - 2
    # set a templated value
    - name: namespace
      value: {{ .Namespace }}
    # will attempt to decrypt it using helm-secrets plugin
    secrets:
      - vault_secret.yaml
    # Override helmDefaults options for verify, wait, waitForJobs, timeout, recreatePods, force and reuseValues.
    verify: true
    keyring: path/to/keyring.gpg
    #  --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint' (default false)
    skipSchemaValidation: false
    wait: true
    # DEPRECATED: waitRetries is no longer supported - see documentation above
    # waitRetries: 3
    waitForJobs: true
    timeout: 60
    recreatePods: true
    force: false
    reuseValues: false
    # set `false` to uninstall this release on sync.  (default true)
    installed: true
    # Defines the strategy to use when updating. Possible value is:
    # - "reinstallIfForbidden": Performs an uninstall before the update only if the update is forbidden (e.g., due to permission issues or conflicts).
    updateStrategy: ""
    # restores previous state in case of failed release (default false).
    # On Helm 4+ this emits --rollback-on-failure (the successor to the deprecated --atomic).
    atomic: true
    # restores previous state on a failed release via the Helm 4 --rollback-on-failure flag
    # (default false). Requires Helm 4 or greater. Mutually exclusive with atomic.
    rollbackOnFailure: false
    # when true, cleans up any new resources created during a failed release (default false)
    cleanupOnFail: false
    # --kube-context to be passed to helm commands
    # See https://github.com/roboll/helmfile/issues/642
    # (default "", which means the standard kubeconfig, either ~/kubeconfig or the file pointed by $KUBECONFIG environment variable)
    kubeContext: kube-context
    # passes --disable-validation to helm diff plugin, this requires diff plugin >= 3.1.2
    # It may be helpful to deploy charts with helm api v1 CRDS
    # https://github.com/roboll/helmfile/pull/1373
    disableValidation: false
    # passes --disable-validation to helm diff plugin, this requires diff plugin >= 3.1.2
    # It is useful when any release contains custom resources for CRDs that is not yet installed onto the cluster.
    # https://github.com/roboll/helmfile/pull/1618
    # To apply this to all releases without editing each one, use the --skip-diff-validation-on-install CLI flag.
    disableValidationOnInstall: false
    # passes --disable-openapi-validation to helm diff plugin, this requires diff plugin >= 3.1.2
    # It may be helpful to deploy charts with helm api v1 CRDS
    # https://github.com/roboll/helmfile/pull/1373
    disableOpenAPIValidation: false
    # limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
    historyMax: 10
    # When set to `true`, skips running `helm dep up` and `helm dep build` on this release's chart.
    # Useful when the chart is broken, like seen in https://github.com/roboll/helmfile/issues/1547
    skipDeps: false
    # propagate `--post-renderer` to helmv3 template and helm install
    postRenderer: "path/to/postRenderer"
    # propagate `--post-renderer-args` to helmv3 template and helm install. This allows using Powershell
    # scripts on Windows as a post renderer
    postRendererArgs:
    - PowerShell
    - "-Command"
    - "theScript.ps1"
    # cascade `--cascade` to helmv3 delete, available values: background, foreground, or orphan, default: background
    cascade: "background"
    # insecureSkipTLSVerify is true if the TLS verification should be skipped when fetching remote chart
    insecureSkipTLSVerify: false
    # plainHttp is true if fetching the remote chart should be done using HTTP
    plainHttp: false
    # suppressDiff skip the helm diff output. Useful for charts which produces large not helpful diff, default: false
    suppressDiff: false
    # suppressOutputLineRegex is a list of regex patterns to suppress output lines from helm diff (default []), available in helmfile v0.162.0
    suppressOutputLineRegex:
      - "version"
    # syncReleaseLabels is a list of labels to be added to the release when syncing.
    syncReleaseLabels: false
    # unitTests is a list of test file or directory paths for helm-unittest integration.
    # When specified, `helmfile unittest` will run `helm unittest` with the merged values and these test paths.
    # Requires the helm-unittest plugin: https://github.com/helm-unittest/helm-unittest
    unitTests:
      - tests/vault


  # Local chart example
  - name: grafana                            # name of this release
    namespace: another                       # target namespace
    chart: ../my-charts/grafana              # the chart being installed to create this release, referenced by relative path to local helmfile
    values:
    - "../../my-values/grafana/values.yaml"             # Values file (relative path to manifest)
    - ./values/{{ requiredEnv "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment.
    wait: true

#
# Advanced Configuration: Nested States
#
helmfiles:
- # Path to the helmfile state file being processed BEFORE releases in this state file
  path: path/to/subhelmfile.yaml
  # Label selector used for filtering releases in the nested state.
  # For example, `name=prometheus` in this context is equivalent to processing the nested state like
  #   helmfile -f path/to/subhelmfile.yaml -l name=prometheus sync
  selectors:
  - name=prometheus
  # Override state values
  values:
  # Values files merged into the nested state's values
  - additional.values.yaml
  # One important aspect of using values here is that they first need to be defined in the values section
  # of the origin helmfile, so in this example key1 needs to be in the values or environments.NAME.values of path/to/subhelmfile.yaml
  # Inline state values merged into the nested state's values
  - key1: val1
- # All the nested state files under `helmfiles:` is processed in the order of definition.
  # So it can be used for preparation for your main `releases`. An example would be creating CRDs required by `releases` in the parent state file.
  path: path/to/mycrd.helmfile.yaml
- # Terraform-module-like URL for importing a remote directory and use a file in it as a nested-state file
  # The nested-state file is locally checked-out along with the remote directory containing it.
  # Therefore all the local paths in the file are resolved relative to the file
  path: git::https://github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0
- # By default git repositories aren't updated unless the ref is updated.
  # Alternatively, refer to a named ref and disable the caching.
  path: git::ssh://git@github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=main&cache=false
# If set to "Error", return an error when a subhelmfile points to a
# non-existent path. The default behavior is to print a warning and continue.
missingFileHandler: Error
missingFileHandlerConfig:
  # Ignores missing git branch error so that the Debug/Info/Warn handler can treat a missing branch as non-error.
  # See https://github.com/helmfile/helmfile/issues/392
  ignoreMissingGitBranch: true

#
# Advanced Configuration: Environments
#

# The list of environments managed by helmfile.
#
# The default is `environments: {"default": {}}` which implies:
#
# - `{{ .Environment.Name }}` evaluates to "default"
# - `{{ .Values }}` being empty
environments:
  # The "default" environment is available and used when `helmfile` is run without `--environment NAME`.
  default:
    # Everything from the values.yaml is available via `{{ .Values.KEY }}`.
    # Suppose `{"foo": {"bar": 1}}` contained in the values.yaml below,
    # `{{ .Values.foo.bar }}` is evaluated to `1`.
    values:
    - environments/default/values.yaml
    # Everything from the values.hcl in the `values` block is available via `{{ .Values.KEY }}`.
    # More details in its dedicated section
    - environments/default/values.hcl
    # Each entry in values can be either a file path or inline values.
    # The below is an example of inline values, which is merged to the `.Values`
    - myChartVer: 1.0.0-dev
  # Any environment other than `default` is used only when `helmfile` is run with `--environment NAME`.
  # That is, the "production" env below is used when and only when it is run like `helmfile --environment production sync`.
  production:
    values:
    - environments/production/values.yaml
    - myChartVer: 1.0.0
    # disable vault release processing
    - vault:
        enabled: false
    ## `secrets.yaml` is decrypted by `helm-secrets` and available via `{{ .Environment.Values.KEY }}`
    secrets:
    - environments/production/secrets.yaml
    # Instructs helmfile to fail when unable to find a environment values file listed under `environments.NAME.values`.
    #
    # Possible values are  "Error", "Warn", "Info", "Debug". The default is "Error".
    #
    # Use "Warn", "Info", or "Debug" if you want helmfile to not fail when a values file is missing, while just leaving
    # a message about the missing file at the log-level.
    missingFileHandler: Error
    missingFileHandlerConfig:
      # Ignores missing git branch error so that the Debug/Info/Warn handler can treat a missing branch as non-error.
      # See https://github.com/helmfile/helmfile/issues/392
      ignoreMissingGitBranch: true
    # kubeContext to use for this environment
    kubeContext: kube-context

#
# Advanced Configuration: Layering
#
# Helmfile merges all the "base" state files and this state file before processing.
#
# Assuming this state file is named `helmfile.yaml`, all the files are merged in the order of:
#   environments.yaml <- defaults.yaml <- templates.yaml <- helmfile.yaml
bases:
- environments.yaml
- defaults.yaml
- templates.yaml

#
# Advanced Configuration: API Capabilities
#
# 'helmfile template' renders releases locally without querying an actual cluster,
# and in this case `.Capabilities.APIVersions` cannot be populated.
# When a chart queries for a specific CRD or the Kubernetes version, this can lead to unexpected results.
#
# Note that `Capabilities.KubeVersion` is deprecated in Helm 3 and `helm template` won't populate it.
# All you can do is fix your chart to respect `.Capabilities.APIVersions` instead, rather than trying to figure out
# how to set `Capabilities.KubeVersion` in Helmfile.
#
# Configure a fixed list of API versions to pass to 'helm template' via the --api-versions flag with the below:
apiVersions:
- example/v1

# Set the kubeVersion to render the chart with your desired Kubernetes version.
# The flag --kube-version was deprecated in helm v3 but it was added again.
# For further information https://github.com/helm/helm/issues/7326
kubeVersion: v1.21

Additional helmDefaults fields

The following helmDefaults fields are also available but not shown in the example above:

Field Type Default Description
enableDNS bool false Enable DNS lookups when rendering templates
skipCRDs bool false Skip CRDs during installation
skipRefresh bool false Skip running helm dependency up
resolveOCIVersions bool true Resolve OCI semver constraints (e.g. ~1, ^2.0.0, *, 1.x, and partial versions like 1.2 that helm treats as floating ranges) to concrete registry tags before deriving the shared cache path. Prevents stale cache hits after new matching tags are published. Set to false to keep the pre-fix behavior. Skipped when skipRefresh is enabled (CLI --skip-refresh, per-release, or helmDefaults). Only affects OCI releases with a non-exact version. See issue #2766
atomic bool false Restore previous state on a failed install/upgrade. On Helm 4+ emits --rollback-on-failure (the successor to the deprecated --atomic); on older Helm emits --atomic
rollbackOnFailure bool false Restore previous state on a failed install/upgrade via the Helm 4 --rollback-on-failure flag. Requires Helm 4 or greater. Mutually exclusive with atomic
forceConflicts bool false Force server-side apply changes against conflicts (Helm 4 only)
takeOwnership bool false Take ownership of existing resources
serverSide string Controls the helm 4 --server-side flag. Must be "true", "false", or "auto" (Helm 4 only)
trackMode string "" Default tracking mode for resources. See Advanced Features
disableAutoDetectedKubeVersionForDiff bool false Disable auto-detected kubeVersion being passed to helm diff

Additional release fields

Condition

condition controls whether a release is enabled. An empty condition enables the release. A direct true or false value is treated as a literal boolean and bypasses values lookup. Any other condition must be a values lookup path ending in .enabled, such as vault.enabled.

conditionTemplate is evaluated before condition is checked. It must render to a boolean value; when both condition and conditionTemplate are set, the rendered conditionTemplate value replaces condition. Like other *Template fields, conditionTemplate is not evaluated by the list command.

The following per-release fields are also available:

Field Type Default Description
valuesTemplate list Like values but template expressions are rendered before being passed to Helm
setTemplate list Like set but template expressions are rendered before being passed to Helm
apiVersions list Per-release API versions (overrides top-level apiVersions)
kubeVersion string Per-release kube version (overrides top-level kubeVersion)
valuesPathPrefix string Prefix for values file paths
verifyTemplate string Templated verify flag (e.g., {{ .Values.verify | default "false" }})
waitTemplate string Templated wait flag
installedTemplate string Templated installed flag
conditionTemplate string Templated condition flag. Must render to a boolean. When set with condition, the rendered value replaces condition
adopt list List of resources to adopt (passes --adopt to Helm)
forceGoGetter bool false Force go-getter URL parsing for the chart field. Useful when go-getter URL parsing fails unexpectedly
forceNamespace string Force namespace on all K8s resources rendered by the chart, even when the template doesn't use {{ .Namespace }}. Use with caution
skipRefresh bool false Per-release skip for helm dependency up
resolveOCIVersions bool inherited Per-release override of the helmDefaults.resolveOCIVersions setting
disableAutoDetectedKubeVersionForDiff bool false Disable auto-detected kubeVersion for helm diff on this release
takeOwnership bool false Take ownership of existing resources for this release
serverSide string Controls the helm 4 --server-side flag for this release. Must be "true", "false", or "auto" (Helm 4 only)
forceConflicts bool false Force server-side apply against conflicts (Helm 4 only)
description string Description of the release
enableDNS bool false Enable DNS lookups when rendering templates

Release tracking fields (kubedog)

See Advanced Features for more details:

Field Type Default Description
trackMode string "" Track mode: helm, helm-legacy, or kubedog
trackTimeout int 300 Tracking timeout in seconds
trackLogs bool false Enable real-time log streaming
trackKinds list Whitelist of resource kinds to track
skipKinds list Blacklist of resource kinds to skip
trackResources list Specific resources to track (objects with kind, name, namespace)
kubedogQPS float QPS for kubedog kubernetes client
kubedogBurst int Burst for kubedog kubernetes client

Hook kubectlApply

Hooks also support a kubectlApply field for running kubectl apply directly:

releases:
- name: myapp
  chart: mychart
  hooks:
  - events: ["presync"]
    showlogs: true
    kubectlApply:
      filename: manifests/my-resource.yaml

Or with kustomize:

  hooks:
  - events: ["presync"]
    showlogs: true
    kubectlApply:
      kustomize: overlays/default/

Repository additional fields

Field Type Description
registryConfig string Path to registry configuration file
managed string Managed repository mode

Template Partials

Files matching _*.tpl in the same directory as the helmfile are automatically loaded as helper templates. For example, a file named _helpers.tpl can define named templates that are reusable across your helmfile:

_helpers.tpl:

{{- define "myapp.labels" -}}
app: myapp
env: {{ .Environment.Name }}
{{- end -}}

helmfile.yaml:

releases:
- name: myapp
  chart: mychart
  values:
  - labels: {{ include "myapp.labels" . | toYaml | nindent 4 }}

LLM Configuration (doctor)

The optional top-level llm block configures the OpenAI-compatible LLM endpoint used by helmfile doctor for AI-assisted diff analysis. When absent, doctor degrades to plain helmfile diff.

llm:
  # baseURL is optional. Defaults to https://api.openai.com/v1.
  # Set this when using a gateway (One-API, LiteLLM, etc.) or non-OpenAI provider.
  baseURL: https://one-api.internal/v1

  # apiKey is required. Use template expressions to pull from env:
  apiKey: {{ env "HELMFILE_LLM_API_KEY" }}

  # model is required (e.g. "gpt-4o", "claude-3-5-sonnet" via gateway, "deepseek-chat").
  model: gpt-4o

  # Optional: per-request timeout (default: 60s).
  timeout: 90s

  # Optional: max completion tokens (default: 4096).
  maxTokens: 8192

Configuration precedence: environment variables (HELMFILE_LLM_*) < this llm: block < CLI flags (--llm-*). See CLI Reference > doctor for the full documentation including secret redaction, exit codes, and backend compatibility.