Skip to content

RetryOnFailure should support a bounded retries config (currently retries forever, never reaches Stalled) #1551

Description

@pulkits15

This is a follow-up to #1278 / #1281, which introduced the RetryOnFailure strategy for .spec.install.strategy and .spec.upgrade.strategy.

Today, RetryOnFailure retries a failed install/upgrade indefinitely at the fixed .retryInterval, with no way to bound the number of attempts. Unlike the RemediateOnFailure strategy — which uses .remediation.retries (0 = no retries, negative = infinite) and transitions the HelmRelease to Stalled with reason RetriesExceeded once retries are exhausted — a HelmRelease using RetryOnFailure can never reach a terminal Stalled state. For a release that is failing for a permanent reason (bad values, invalid chart, misconfigured cluster), this means the controller reconciles forever with no signal that human intervention is needed.

This has real operational cost: #1359 showed that unbounded RetryOnFailure loops were already filling up .status.history (and etcd) before that specific growth bug was fixed — a symptom of retrying forever with no upper bound or off-ramp.

Proposal

Add an optional .retries field to .spec.install.strategy and .spec.upgrade.strategy (alongside the existing .retryInterval), with the same semantics already used by .remediation.retries elsewhere in this API:

  • A non-negative value caps the number of retry attempts before the controller stops and marks the HelmRelease Stalled (reason RetriesExceeded, consistent with how RemediateOnFailure already reports this).
  • A negative value (default: -1) preserves today's behavior of infinite retries, so this is fully backward compatible.

API Changes

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
spec:
  install:
    strategy:
      name: RetryOnFailure
      retryInterval: 5m
      retries: -1 # defaults to -1 (infinite, current behavior); a non-negative value caps attempts before Stalled
  upgrade:
    strategy:
      name: RetryOnFailure
      retryInterval: 5m
      retries: -1 # same semantics as install.strategy.retries

Note on naming: I've named this field .retries to match the existing .remediation.retries convention (negative = infinite). Since .remediation.retries is documented as ignored when the strategy is RetryOnFailure, open to alternative names (e.g. maxRetries) if maintainers feel a second field literally named retries in a different location would be confusing — happy to go either way.

Why this matters

This brings parity between RetryOnFailure and RemediateOnFailure: both strategies would support bounded retries with a consistent, discoverable terminal state (Stalled / RetriesExceeded), instead of RetryOnFailure being the only strategy that can never surface "this needs attention."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions