Aller au contenu
Conteneurs & Orchestration medium

Helm v4 : changements, breaking changes et migration depuis Helm v3

40 min de lecture

logo helm

Helm v4 apporte des changements importants qui peuvent casser vos pipelines CI/CD si vous ne les anticipez pas. Ce guide liste tout ce qui change, avec les commandes exactes pour adapter vos scripts. Vous apprendrez à identifier les incompatibilités, tester votre migration en staging, et basculer sans incident en production.

  • Identifier les flags renommés et les corriger (--atomic--rollback-on-failure)
  • Migrer vos post-renderers vers le système de plugins
  • Comprendre le nouveau comportement de --dry-run et --wait
  • Activer Server-Side Apply (SSA) et gérer les conflits
  • Adapter l'authentification OCI et utiliser les digests
  • Planifier votre migration avec une checklist CI/CD

Avant de lire le détail, voici une checklist des breaking changes à vérifier dans vos scripts :

Ce qui changev3v4Impact
Rollback automatique--atomic--rollback-on-failureScripts CI/CD
Forcer remplacement--force--force-replaceScripts upgrade
Post-renderer--post-renderer ./script--post-renderer plugin-namePipelines avec kustomize
Dry-run--dry-run (booléen)--dry-run=client|serverTests avant déploiement
Wait--wait (booléen)--wait=watcher|legacyAttente pods ready
Server-Side ApplyOpt-inActivé par défautConflits ownership
OCI loginURL complète acceptéeDomaine uniquementAuth registries

Les ruptures les plus visibles portent sur la ligne de commande, et elles se manifestent de deux façons très différentes. Un flag supprimé provoque une erreur immédiate (unknown flag), donc un pipeline en échec franc. Un flag dont le comportement change, comme --dry-run ou --wait, passe au contraire inaperçu et modifie silencieusement ce que fait votre déploiement. Les seconds méritent plus d'attention que les premiers.

Trois flags de helm upgrade sont concernés. Le premier reste accepté mais déprécié, le deuxième a changé de nom, le troisième est nouveau et lié à Server-Side Apply.

Le flag --atomic existe toujours mais est déprécié. Utilisez --rollback-on-failure :

Fenêtre de terminal
helm upgrade --install myapp ./chart \
--atomic \
--wait \
--timeout 5m

Comportement : Si le déploiement échoue (pods non Ready dans le timeout), Helm effectue automatiquement un rollback vers la révision précédente.

Le flag pour forcer le remplacement des ressources a été renommé :

Fenêtre de terminal
helm upgrade myapp ./chart --force

Quand l'utiliser : Forcer le remplacement complet d'une ressource au lieu d'un patch. Utile quand un champ immutable a changé.

En v4, un nouveau flag gère les conflits Server-Side Apply :

Fenêtre de terminal
helm upgrade myapp ./chart --force-conflicts

Ce flag force l'application même si un autre controller (ArgoCD, Flux, kubectl) a modifié la ressource.


Un post-renderer est un programme qui reçoit les manifestes générés par Helm et les transforme avant leur envoi au cluster ; c'est le point d'accroche habituel de kustomize. C'est le changement de la v4 qui casse le plus de pipelines, parce qu'il ne se contourne pas : le passage par le système de plugins est obligatoire, et il faut donc empaqueter le script existant avant de migrer.

En Helm v3, vous pouviez passer un binaire externe :

Fenêtre de terminal
# v3 : binaire direct (NE FONCTIONNE PLUS en v4)
helm template myapp ./chart --post-renderer ./kustomize-overlay.sh

En Helm v4, les post-renderers passent obligatoirement par le système de plugins :

Fenêtre de terminal
# v4 : plugin installé
helm template myapp ./chart --post-renderer kustomize

La conversion ne touche pas au script lui-même : il continue de lire les manifestes sur l'entrée standard et d'écrire le résultat sur la sortie standard. Ce qui change, c'est son emballage : un répertoire dans le dossier des plugins et un fichier plugin.yaml qui le déclare avec postRenderer: true.

  1. Convertir votre script en plugin Helm

    Créez la structure de plugin :

    Fenêtre de terminal
    mkdir -p ~/.local/share/helm/plugins/my-postrenderer

    Créez plugin.yaml :

    ~/.local/share/helm/plugins/my-postrenderer/plugin.yaml
    name: "my-postrenderer"
    version: "1.0.0"
    usage: "Post-render manifests with custom logic"
    command: "$HELM_PLUGIN_DIR/render.sh"
    postRenderer: true
  2. Déplacer votre script

    Copiez votre script existant :

    Fenêtre de terminal
    cp ./kustomize-overlay.sh ~/.local/share/helm/plugins/my-postrenderer/render.sh
    chmod +x ~/.local/share/helm/plugins/my-postrenderer/render.sh
  3. Vérifier l'installation

    Fenêtre de terminal
    helm plugin list

    Sortie attendue :

    NAME VERSION DESCRIPTION
    my-postrenderer 1.0.0 Post-render manifests with custom logic
  4. Utiliser le plugin

    Fenêtre de terminal
    helm template myapp ./chart --post-renderer my-postrenderer

Utilisez --post-renderer-args :

Fenêtre de terminal
helm upgrade --install myapp ./chart \
--post-renderer kustomize \
--post-renderer-args "--enable-helm" \
--post-renderer-args "--load-restrictor=LoadRestrictionsNone"

--dry-run est le flag le plus dangereux de cette migration, car il ne provoque aucune erreur : la commande continue de fonctionner, mais pas forcément comme avant. En v3 c'était un booléen ; en v4 il accepte une valeur qui détermine si Helm contacte ou non le cluster. Un pipeline de validation qui croyait tester contre le serveur peut se retrouver à ne valider que localement.

En v3, --dry-run était un booléen. En v4, c'est une option à valeurs :

ValeurComportementConnexion cluster
--dry-run=noneExécution normale (défaut si omis)Oui
--dry-run=clientSimulation côté client uniquementNon
--dry-run=serverSimulation côté serveurOui
--dry-run (sans valeur)Équivalent à --dry-run=clientNon

Recommandation : Utilisez toujours la forme explicite pour éviter les ambiguïtés.

Fenêtre de terminal
# Validation sans cluster (rapide, offline)
helm upgrade --install myapp ./chart --dry-run=client
# Validation avec le cluster (vérifie schémas, RBAC, quotas)
helm upgrade --install myapp ./chart --dry-run=server

Le flag --hide-secret masque les valeurs des Secrets dans la sortie :

Fenêtre de terminal
helm template myapp ./chart --dry-run=client --hide-secret

Le Server-Side Apply est un mécanisme de Kubernetes où c'est l'API server, et non le client, qui fusionne la ressource souhaitée avec celle déjà en place. Chaque champ garde la trace du propriétaire qui l'a défini, ce qui permet au cluster de refuser une modification venant d'un autre gestionnaire. Ce changement est celui qui produit les erreurs les plus déroutantes en migration, surtout si un outil GitOps gère déjà les mêmes ressources.

En v3 : Client-Side Apply (kubectl apply côté client) En v4 : Server-Side Apply activé par défaut (--server-side=true)

SSA délègue la logique de merge au serveur Kubernetes, ce qui apporte :

  • Meilleure gestion des conflits : détection si un autre controller a modifié la ressource
  • Ownership tracking : Kubernetes sait qui a créé chaque champ
  • Dry-run serveur : validation plus précise

Si un autre outil (ArgoCD, kubectl, opérateur) a modifié une ressource, vous verrez :

Error: UPGRADE FAILED: cannot patch "myapp" with kind Deployment:
Apply failed with 1 conflict: conflict with "argocd-application-controller"

Solutions :

  1. Forcer l'ownership (si Helm doit prendre le contrôle) :

    Fenêtre de terminal
    helm upgrade myapp ./chart --force-conflicts
  2. Désactiver SSA (temporairement, pour debug) :

    Fenêtre de terminal
    helm upgrade myapp ./chart --server-side=false

Une simulation côté serveur suffit à savoir comment Helm applique vos ressources, sans rien modifier dans le cluster.

Fenêtre de terminal
helm upgrade myapp ./chart --dry-run=server 2>&1 | grep -i "server-side"

--wait décide si helm upgrade rend la main immédiatement ou attend que les ressources soient réellement prêtes. En v4, ce n'est plus un interrupteur mais un choix de stratégie de surveillance, et la valeur par défaut n'attend que les hooks. Un pipeline qui comptait sur --wait pour bloquer jusqu'au démarrage effectif des pods doit donc être adapté, sous peine de déclarer un déploiement réussi avant qu'il ne le soit.

En v3, --wait attendait que les ressources soient "Ready" selon une logique interne. En v4, vous choisissez la stratégie :

ValeurComportement
--wait=hookOnlyAttend seulement les hooks (défaut)
--wait=watcherUtilise kstatus pour surveiller l'état réel
--wait=legacyComportement v3 (rétrocompatibilité)

Le mode watcher utilise kstatus qui comprend mieux l'état des ressources Kubernetes :

  • Détecte les CRDs avec des conditions personnalisées
  • Comprend les états intermédiaires (Progressing, Degraded)
  • Gère les ressources non-standard (Argo Rollouts, Knative, etc.)
Fenêtre de terminal
helm upgrade --install myapp ./chart \
--wait=watcher \
--timeout 5m

Le flag --wait-for-jobs reste disponible :

Fenêtre de terminal
helm upgrade --install myapp ./chart \
--wait=watcher \
--wait-for-jobs \
--timeout 10m

Helm publie et récupère les charts depuis des registries OCI, les mêmes que celles qui hébergent vos images de conteneurs. Deux points changent en v4 : la commande de connexion devient plus stricte sur ce qu'elle accepte, et l'installation peut désormais viser un digest plutôt qu'un tag. Le premier casse les scripts existants, le second améliore la reproductibilité des déploiements.

En v4, helm registry login attend uniquement le domaine, pas une URL complète :

Fenêtre de terminal
# Fonctionnait en v3
helm registry login oci://ghcr.io/myorg/charts

Pattern CI/CD recommandé :

Fenêtre de terminal
echo "$REGISTRY_TOKEN" | helm registry login ghcr.io -u "$REGISTRY_USER" --password-stdin

Helm v4 supporte l'installation par digest SHA256 pour des déploiements reproductibles :

Fenêtre de terminal
# Par tag (peut changer si le tag est réécrit)
helm install myapp oci://ghcr.io/myorg/charts/myapp --version 1.2.3
# Par digest (immuable, recommandé pour prod)
helm install myapp oci://ghcr.io/myorg/charts/myapp@sha256:abc123...

Obtenir le digest d'un chart :

Fenêtre de terminal
helm pull oci://ghcr.io/myorg/charts/myapp --version 1.2.3 2>&1 | grep Digest

La migration se fait sans désinstaller Helm v3 : les deux binaires cohabitent sous des noms différents, ce qui permet de comparer les manifestes générés avant de basculer quoi que ce soit. L'ordre des étapes compte, car l'audit des scripts détermine l'ampleur du travail restant, notamment la présence ou non d'un post-renderer à convertir.

  1. Auditer vos scripts

    Cherchez les patterns à mettre à jour :

    Fenêtre de terminal
    # Dans vos repos CI/CD
    grep -rn --include="*.yml" --include="*.yaml" --include="*.sh" \
    -E "(--atomic|--force[^-]|--post-renderer [^-]|--dry-run[^=])" .
  2. Installer Helm v4 en parallèle

    Testez sans remplacer v3. Téléchargez l'archive et le fichier de sommes officiel publié à côté d'elle, vérifiez l'empreinte, et n'extrayez qu'ensuite :

    Fenêtre de terminal
    HELM_VERSION="4.1.0"
    curl -fsSLO "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz"
    curl -fsSLO "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz.sha256sum"
    sha256sum --check "helm-v${HELM_VERSION}-linux-amd64.tar.gz.sha256sum"
    # helm-v4.1.0-linux-amd64.tar.gz: OK

    Tant que la vérification ne répond pas OK, n'installez rien. Une archive dépliée directement depuis le réseau ne laisse aucune chance de détecter une substitution :

    Fenêtre de terminal
    tar -xzf "helm-v${HELM_VERSION}-linux-amd64.tar.gz"
    sudo install -m 0755 linux-amd64/helm /usr/local/bin/helm4
    # Tester
    helm4 version
  3. Tester en environnement de dev

    Fenêtre de terminal
    # Comparer les manifests générés
    helm3 template myapp ./chart -f values.yaml > v3-manifests.yaml
    helm4 template myapp ./chart -f values.yaml > v4-manifests.yaml
    diff v3-manifests.yaml v4-manifests.yaml
  4. Migrer les post-renderers

    Si vous utilisez --post-renderer, convertissez en plugin (voir section dédiée).

  5. Adapter les flags dans CI/CD

    Mettez à jour vos pipelines :

    .gitlab-ci.yml
    deploy:
    script:
    # Avant (v3)
    # - helm upgrade --install myapp ./chart --atomic --wait
    # Après (v4)
    - helm upgrade --install myapp ./chart --rollback-on-failure --wait=watcher
  6. Déployer en staging

    Testez le cycle complet :

    • Install → Upgrade → Rollback → Uninstall
  7. Basculer en production

    Une fois validé en staging, mettez à jour Helm et les scripts en production.


Les messages ci-dessous se répartissent en deux familles. Les erreurs unknown flag sont bénignes : le pipeline s'arrête net, la correction est mécanique. Les conflits Server-Side Apply et les problèmes de détection d'état demandent une décision, car ils révèlent qu'un autre acteur gère les mêmes ressources ou que l'attente ne portait pas sur ce que vous croyiez.

SymptômeCauseSolution
unknown flag: --atomicHelm v4 avec ancien flagRemplacer par --rollback-on-failure
unknown flag: --forceHelm v4 avec ancien flagRemplacer par --force-replace
Apply failed with conflictSSA détecte une modification externeUtiliser --force-conflicts ou coordonner l'ownership
post-renderer plugin not foundPost-renderer non migréConvertir en plugin Helm
Error: login requires exactly one argumentURL au lieu du domaineUtiliser seulement le domaine (ghcr.io)
--dry-run flag needs an argumentSyntaxe v3Utiliser --dry-run=client ou --dry-run=server
Pods non détectés comme ReadyMode wait legacyUtiliser --wait=watcher

Premier réflexe avant tout diagnostic : la sortie indique aussi la version du client Kubernetes embarquée, qui conditionne la compatibilité avec votre cluster.

Fenêtre de terminal
helm version

Sortie attendue (v4) :

version.BuildInfo{Version:"v4.1.0", GitCommit:"...", GoVersion:"go1.25.6", KubeClientVersion:"v1.35"}

Si vous devez rester compatible temporairement :

Fenêtre de terminal
helm upgrade --install myapp ./chart \
--server-side=false \
--wait=legacy

ChangementAction requise
--atomic--rollback-on-failureRechercher/remplacer dans tous les scripts
--force--force-replaceRechercher/remplacer
Post-renderer binaire → pluginCréer un plugin Helm
--dry-run--dry-run=client|serverSpécifier explicitement la valeur
SSA activé par défautGérer les conflits ownership
--wait--wait=watcherUtiliser pour meilleure détection Ready
OCI login : domaine seulAdapter les scripts de login

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn