CtrlK
BlogDocsLog inGet started
Tessl Logo

nitinjain999/platform-skills

Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.

67

Quality

84%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

fluxcd-helmrelease.mdreferences/

FluxCD HelmRelease Reference

HelmRelease (helm.toolkit.fluxcd.io/v2) is managed by helm-controller. It installs, upgrades, tests, and garbage-collects Helm releases with drift detection and structured remediation.


Minimal example

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
  namespace: cert-manager
spec:
  interval: 10m
  chartRef:
    kind: OCIRepository
    name: cert-manager
    namespace: flux-system
  values:
    installCRDs: true
  install:
    strategy:
      name: RetryOnFailure    # always use this

Chart source — two mutually exclusive approaches

ApproachWhen to useNotes
spec.chartRefOCI chart sources (recommended)References an existing OCIRepository or HelmChart; supports Cosign verification
spec.chart.specHTTPS Helm repos / GitRepositoryAuto-creates a HelmChart object; cannot be used with spec.chartRef

Using spec.chartRef (OCI — recommended)

spec:
  chartRef:
    kind: OCIRepository
    name: cert-manager
    namespace: flux-system

Using spec.chart.spec (HTTPS repo)

spec:
  chart:
    spec:
      chart: cert-manager
      version: ">=1.13.0"
      sourceRef:
        kind: HelmRepository
        name: jetstack
        namespace: flux-system

Do not set both — they are mutually exclusive.


Values merging

Sources are applied in sequence, each layer overriding the previous:

spec:
  valuesFrom:
    - kind: ConfigMap
      name: cert-manager-values       # base values
      valuesKey: values.yaml
    - kind: Secret
      name: cert-manager-secrets      # sensitive overrides
      valuesKey: values.yaml
      optional: true
  values:
    # inline values — applied last, highest precedence
    replicaCount: 2

ConfigMaps/Secrets used in valuesFrom should carry:

labels:
  reconcile.fluxcd.io/watch: Enabled    # triggers immediate HelmRelease reconciliation on change

Install / upgrade strategy

Always use RetryOnFailure — it retries without uninstalling or rolling back, avoiding downtime and data loss:

spec:
  install:
    strategy:
      name: RetryOnFailure
      retryInterval: 3m    # how long to wait between retries (default: interval)
  upgrade:
    strategy:
      name: RetryOnFailure
      retryInterval: 3m
    cleanupOnFail: true
    force: false    # set true only for immutable field changes

Set retryInterval shorter than spec.interval for faster recovery from transient failures. Without it, Flux waits the full reconciliation interval between retries.

Do not mix modern install.strategy with legacy install.remediation.retries — they are mutually exclusive.


Drift detection

ModeBehaviour
disabledNo detection (default)
warnDetects and reports but does not correct
enabledDetects and corrects on every reconciliation
spec:
  driftDetection:
    mode: enabled
    ignore:
      - paths:
          - /spec/replicas    # exclude HPA-managed field
        target:
          kind: Deployment

CRD lifecycle

ValueInstallUpgrade
Create (default)Installs CRDsSkips — does not upgrade existing CRDs
SkipSkips CRDsSkips CRDs
CreateReplaceInstalls CRDsReplaces CRDs — use for breaking CRD upgrades
spec:
  install:
    crds: CreateReplace
  upgrade:
    crds: CreateReplace

Dependencies

Wait until listed HelmReleases or Kustomizations are Ready before proceeding:

spec:
  dependsOn:
    - name: cert-manager
      namespace: cert-manager
    - name: ingress-nginx
      namespace: ingress-nginx

Health check expressions

CEL expressions for custom resource readiness — evaluated when wait: true is active:

spec:
  healthCheckExprs:
    - apiVersion: apps/v1
      kind: Deployment
      current: >
        object.status.conditions.filter(e, e.type == 'Available').all(e, e.status == 'True')
      inProgress: >
        object.status.conditions.filter(e, e.type == 'Progressing').exists(e, e.status == 'True')
      failed: >
        object.status.replicas > 0 && object.status.readyReplicas == 0

Post-renderers

Kustomize patches applied after Helm templating, before apply. Useful for injecting annotations, sidecars, or overriding fields the chart doesn't expose as values:

spec:
  postRenderers:
    - kustomize:
        patches:
          - target:
              kind: Deployment
              name: ".*"
            patch: |
              - op: add
                path: /spec/template/metadata/annotations/prometheus.io~1scrape
                value: "true"
          - target:
              kind: Ingress
              name: ".*"
            patch: |
              - op: add
                path: /metadata/annotations/nginx.ingress.kubernetes.io~1ssl-redirect
                value: "true"

Multiple post-renderers execute sequentially — each renderer's output feeds into the next.


Remote cluster deployment

spec:
  kubeConfig:
    secretRef:
      name: remote-cluster-kubeconfig
      key: value

Status tracking

status:
  conditions:
    - type: Ready
      status: "True"
  history:
    - chartVersion: "1.13.3"
      digest: sha256:abc123...
      firstDeployed: "2024-01-15T10:00:00Z"
      lastDeployed: "2024-01-20T14:30:00Z"
  lastAppliedRevision: "1.13.3+sha256:abc123"
  lastAttemptedRevision: "1.13.3+sha256:abc123"

Validation

# Status overview
flux get helmreleases -A

# Detailed reconciliation trace
kubectl describe helmrelease cert-manager -n cert-manager
flux logs --kind=HelmRelease --name=cert-manager --namespace=cert-manager

# Force immediate reconciliation
flux reconcile helmrelease cert-manager -n cert-manager --with-source

# Check rendered values
kubectl get secret -n cert-manager sh.helm.release.v1.cert-manager.v1 -o jsonpath='{.data.release}' | base64 -d | base64 -d | gunzip | jq '.config'

Namespace management

Never use targetNamespace or createNamespace on a HelmRelease. These bypass proper namespace lifecycle management — the namespace is created outside GitOps control and won't be pruned correctly.

Instead, create the target namespace in the parent Kustomization or ResourceSet that deploys the component:

# ❌ Wrong — namespace created outside lifecycle control
spec:
  targetNamespace: cert-manager
  install:
    createNamespace: true

# ✅ Correct — namespace owned by the Kustomization
# In the Kustomization that owns this HelmRelease, include a Namespace manifest:
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager

The namespace must exist before the HelmRelease runs — create it in the parent Kustomization or ResourceSet, never in the HelmRelease itself.


Common mistakes

MistakeCorrect approach
spec.chart.spec with spec.chartRef setMutually exclusive — use one only
install.remediation.retries (legacy)Use install.strategy.name: RetryOnFailure
valuesFrom ConfigMap without watch labelAdd reconcile.fluxcd.io/watch: Enabled or changes won't trigger reconciliation
driftDetection.mode: enabled without ignore rulesHPA-managed replicas will fight with Flux — always ignore /spec/replicas
targetNamespace + createNamespace: true on HelmReleaseCreate the namespace in the parent Kustomization/ResourceSet instead
No retryInterval on strategyFlux waits the full spec.interval between retries — set strategy.retryInterval: 3m for faster recovery

BEFORE_AFTER.md

CHANGELOG.md

CODE_OF_CONDUCT.md

COMMANDS.md

CONTRIBUTING.md

EDITOR_INTEGRATIONS.md

GETTING_STARTED.md

HOW_IT_WORKS.md

install.sh

INSTALLATION.md

LAUNCH.md

PROMPTS.md

QUICKSTART.md

README.md

renovate.json

SECURITY.md

SKILL.md

tessl.json

tile.json