WordPress
22 juillet 2026

Migrer un PrestaShop sur Kubernetes : les erreurs classiques qui font dérailler la mise en prod

Laptop screen shows a Kubernetes dashboard with pods, deployments and metrics; a thinking emoji overlay foregrounds the display.

Sur le papier, migrer un PrestaShop d’un Docker local vers un cluster Kubernetes piloté en GitOps, c’est « juste » déplacer des conteneurs et importer une base. En pratique, le front s’affiche, les premières commandes passent, tout le monde respire… et c’est précisément là que les vrais problèmes commencent. Parce que sur K8s, ce ne sont pas les erreurs spectaculaires qui font mal. Ce sont les pièges discrets : une extension PHP compilée à moitié, un volume monté au mauvais endroit, un déploiement bloqué par une ressource sans rapport. Voici ceux qu’on retrouve à chaque migration, et la checklist des vérifications qu’on oublie systématiquement.

Ce que vous allez apprendre :

  • Pourquoi « le front marche » est le faux positif le plus dangereux d’une migration K8s
  • Les erreurs d’image (GD sans WebP), de volumes (assets de modules masqués) et de réseau (redirections multishop) qui passent inaperçues
  • Comment un déploiement GitOps peut tout bloquer à cause d’une ressource sans rapport
  • Notre checklist des vérifications « pas classiques » à passer après chaque mise en prod

C’est un type de projet qu’on mène régulièrement : reprendre une boutique qui tournait en local sous Docker (ou chez un hébergeur classique) et la porter sur une infrastructure Kubernetes moderne, déployée en GitOps. Sur l’exemple qui nous sert de fil rouge ici, il s’agissait d’un multishop à deux enseignes, avec catalogue conséquent et plusieurs modules métier.

Le scénario est presque toujours le même. Une fois la base importée et les conteneurs démarrés, la boutique s’affiche. Les pages produit chargent, on peut ajouter au panier. On a envie de conclure que la migration est réussie.

Sur une migration Kubernetes, « le front s’affiche » ne prouve presque rien. Le front est servi par le navigateur et le reverse proxy ; le back-office, les PDF et les traitements d’image, eux, dépendent de la pile PHP réelle. Et c’est là que les bombes sont amorcées.

Voici les pièges, dans l’ordre où ils nous tombent dessus.

Erreur n°1 — L’image PHP compilée « à moitié » : le cas GD sans WebP

C’est le piège le plus vicieux, et de loin. Quand on construit une nouvelle image Docker pour le cluster, on recompile les extensions PHP. Si l’extension GD est compilée sans le support WebP (il manque libwebp-dev et le flag --with-webp au moment du docker-php-ext-configure gd), alors toute fonction PHP qui touche une image WebP part en fatale.

👉 Le problème : sur beaucoup de catalogues, une grande partie des images sont en réalité des WebP… renommés en .jpg. Le navigateur et le reverse proxy les servent sans jamais solliciter PHP. Donc le front a l’air parfaitement sain.

Mais dès que PHP doit traiter ces images, ça casse :

  • l’ouverture d’une commande qui génère une facture PDF avec une vignette produit ;
  • la régénération des miniatures depuis le back-office ;
  • les logos de facture ou d’e-mail (souvent eux aussi des WebP en .jpg) ;
  • certains tunnels de paiement ou de remboursement qui produisent une image → page blanche.

Le pire défaut n’est pas celui qui casse tout de suite. C’est celui qui laisse croire que « ça marche » parce que la partie visible fonctionne, pendant que le back-office et les traitements serveur partent en fatale en silence.

👉 La vérification qui sauve, à passer systématiquement sur le conteneur PHP du cluster :

php -r 'var_dump(gd_info()["WebP Support"]);'
// doit afficher bool(true)

Si le catalogue contient des WebP (même déguisés en .jpg), c’est non négociable. Le correctif est côté Dockerfile : ajouter libwebp-dev et reconfigurer GD avec --with-webp, puis rebuild. C’est exactement le genre de détail d’environnement qu’on traque aussi quand on diagnostique un back-office PrestaShop lent : le symptôme est spectaculaire, la cause tient dans un flag de compilation.

Erreur n°2 — Les volumes persistants qui masquent les fichiers de l’image

Sur Kubernetes, on monte des volumes persistants (PVC) pour les données qui doivent survivre au redémarrage d’un pod : les images uploadées, les fichiers téléchargeables, etc. Jusqu’ici, tout va bien.

Le piège apparaît quand on monte un PVC par-dessus un dossier qui contenait déjà des fichiers livrés dans l’image Docker. Typiquement, des dossiers de JS/CSS statiques de certains modules. Le montage masque le contenu de l’image : le pod voit un dossier vide à la place des fichiers attendus.

👉 Et là, le symptôme est déroutant. PrestaShop, via addJS()/addCSS(), fait un file_exists() avant d’émettre la balise. Le fichier n’existe pas (masqué par le volume vide) → la balise <script> n’est jamais émise → aucune erreur 404, juste une fonctionnalité JS qui n’existe plus. On se retrouve avec un classique easyZoom is not a function, un carrousel mort, un effet parallax disparu… sans la moindre erreur réseau pour vous mettre sur la piste.

Un volume persistant ne se contente pas d’ajouter des données : il recouvre ce qui se trouvait dans l’image au même chemin. Si vous montez sur un dossier non vide, vous masquez ce qu’il contenait — silencieusement.

👉 La règle : la copie d’assets vers les PVC ne doit pas se limiter aux dossiers d’upload « évidents ». Tout dossier monté en volume qui contenait des fichiers statiques livrés par l’image doit être réalimenté avec ces fichiers. Et après migration, on vérifie que chaque dossier monté contient bien les assets attendus, plutôt que de supposer.

Erreur n°3 — Multishop : la redirection www / sans-www

En multiboutique, chaque host servi par l’ingress doit avoir une entrée correspondante dans la configuration des URL de boutique (ps_shop_url). Si une variante de domaine n’est pas déclarée — par exemple la version sans www, ou au contraire avec www — PrestaShop ne sait pas à quelle boutique la rattacher.

👉 Résultat : le host non déclaré retombe sur la boutique par défaut. Concrètement, un visiteur qui tape l’URL de la seconde enseigne se retrouve redirigé vers la première. En multishop, c’est une fuite de trafic et un bug de référencement, pas un simple détail cosmétique.

La parade est simple à énoncer : pour chaque boutique, déclarer chaque variante de host servie par l’ingress (avec et sans www), et tester chacune d’elles individuellement.

Après avoir corrigé une redirection, retestez toujours en navigation privée. Les redirections 301 sont mises en cache très agressivement par le navigateur : vous croirez que le bug persiste alors qu’il est corrigé, ou l’inverse.

Erreur n°4 — Des volumes sous-dimensionnés

On a tendance à réserver les volumes « au plus juste » pour ne pas gaspiller. C’est une fausse économie quand on importe une vraie base de production.

👉 Le cas typique : un volume de base de données dimensionné à 5 Gi, saturé par un dump qui pèse plus que prévu une fois les index et les tables temporaires d’import pris en compte. L’import échoue en cours de route avec un disque plein, et on perd du temps à chercher un problème SQL alors que c’est une question d’espace disque.

La même prudence vaut pour le volume de données applicatives (images, fichiers). On dimensionne sur la base du vrai volume de prod, avec une marge confortable — pas sur une estimation optimiste.

Erreur n°5 — Le déploiement GitOps bloqué « en bloc »

C’est une spécificité du déploiement déclaratif (FluxCD, Argo CD…) qui surprend ceux qui débutent sur K8s. L’outil applique souvent un lot de ressources comme une unité. Si une seule ressource du lot est invalide, le dry-run échoue, et l’outil refuse d’appliquer tout le lot — y compris les ressources parfaitement saines.

👉 Le scénario vécu : un volume sans aucun rapport avec PrestaShop (celui d’un autre service hébergé dans le même environnement) avait un manifeste déclarant une taille inférieure à la taille réelle. Tentative de réduction interdite → manifeste invalide → l’ensemble du lot de production refusé. Conséquence : la boutique ne se déployait pas, à cause d’une ressource qui n’avait rien à voir avec elle.

En GitOps, une migration ne se vérifie pas seulement au niveau de votre application. Une ressource invalide ailleurs dans le même périmètre peut bloquer le déploiement entier. Le premier réflexe de debug, c’est l’état de réconciliation global, pas les logs de votre pod.

👉 La vérification : s’assurer que la réconciliation du périmètre de production est au vert (Ready) avant de conclure quoi que ce soit sur votre application. Un déploiement qui « ne fait rien » est souvent un déploiement qui a été refusé en amont.

Erreur n°6 — Les états hérités du clone

Une boutique migrée arrive avec tout son état : ses réglages, ses caches… et parfois des états qu’on n’avait pas anticipés.

  • Mode maintenance hérité. Le clone arrive souvent en maintenance (réponse 503 hors liste d’IP autorisées). En interne, on ne voit rien d’anormal car notre IP est whitelistée. Penser à désactiver la maintenance au moment du go-live, et tester depuis une connexion externe.
  • Identité cloud des modules de comptes. Après un clone, les modules liés à une identité cloud (comptes PrestaShop / event bus) peuvent afficher un avertissement et nécessiter une réinitialisation pour se ré-associer. À traiter même si l’impact fonctionnel est nul, pour ne pas laisser traîner d’alertes.

Erreur n°7 — L’automatisation d’image qu’on croit active

Dans une chaîne moderne, c’est la CI qui build l’image au push, et le cluster qui se met à jour automatiquement quand un nouveau tag apparaît. Deux pièges fréquents :

  • le job de build de l’environnement de production était resté en déclenchement manuel → aucune nouvelle image n’était produite automatiquement ;
  • la politique de mise à jour d’image affiche « no tags » tant que le scan du registre n’est pas peuplé → le cluster reste épinglé sur l’ancien tag, et vos correctifs ne partent jamais en prod.

👉 Après migration, on vérifie concrètement que pousser un correctif produit bien une nouvelle image et que le cluster la récupère tout seul. Sinon, on déploie dans le vide.

Erreur n°8 — Le TLS : savoir qui le termine avant de le configurer

Dans notre configuration de référence, le TLS est géré dans le cluster par cert-manager, avec des certificats Let’s Encrypt délivrés automatiquement via l’ingress NGINX : un ClusterIssuer, l’annotation cert-manager.io/cluster-issuer sur l’ingress, et un challenge HTTP-01. C’est robuste, automatique, et c’est notre défaut.

👉 Le piège, c’est de supposer que ce schéma sera identique partout. Sur certaines plateformes managées, la terminaison TLS est assurée en amont par le load balancer de la plateforme. Dans ce cas, vouloir en plus délivrer des certificats dans le cluster devient au mieux redondant, au pire conflictuel (deux acteurs qui tentent de répondre au même challenge). La règle : déterminer qui termine réellement le TLS avant de configurer, et ne jamais le gérer à deux endroits.

👉 À vérifier aussi : la configuration DNS. Quand l’accès passe par un load balancer de plateforme, les enregistrements pointent en CNAME vers son nom — sauf à l’apex du domaine, où le CNAME est interdit et où il faut un ALIAS / enregistrement A. C’est une source classique de « ça marche sur le sous-domaine mais pas sur le domaine racine ».

Notre checklist post-migration « pas classique »

Les vérifications de base (le site répond, on peut commander) ne suffisent pas. Voici celles qu’on ajoute systématiquement, parce que ce sont elles qui attrapent les bugs silencieux :

  1. Support WebP de GD actif sur le conteneur PHP, si le catalogue contient des WebP (même renommés .jpg).
  2. Assets de modules : chaque dossier monté en volume contient bien les fichiers statiques livrés par l’image (JS/CSS de modules compris).
  3. URL de boutique : chaque host (avec et sans www) de chaque boutique est déclaré et testé individuellement.
  4. Logos facture / e-mail / header : vérifier le rendu en PDF et en e-mail (souvent des WebP piégés).
  5. Dimensionnement des volumes (base + données) prévu sur le vrai volume de prod, avec marge.
  6. État de réconciliation GitOps du périmètre de prod au vert.
  7. Mode maintenance désactivé au go-live, testé depuis une IP externe.
  8. Modules d’identité cloud ré-associés (pas d’avertissement résiduel).
  9. Cache des 301 : retests en navigation privée après toute correction de redirection.
  10. Chaîne d’image automatique : un correctif poussé produit bien une image, et le cluster la récupère seul.
  11. TLS / DNS : terminaison TLS non dupliquée, et apex du domaine en ALIAS/A si l’accès passe par un load balancer.

Ces pièges, on les a transformés en garde-fous

Si on les liste avec autant de précision, c’est qu’on les a rencontrés — et surtout, qu’on a fait en sorte de ne plus les subir. La plupart ne relèvent ni de la chance ni de la vigilance individuelle : elles sont neutralisées par défaut dans notre socle d’infrastructure standardisé.

  • Le masquage d’assets par les volumes → un initContainer recopie systématiquement le contenu de l’image dans le volume au démarrage du pod (un cp -a au bootstrap, conditionné à un volume vide). Le volume ne masque plus les fichiers livrés par l’image : il les contient.
  • Le TLS bricolé → cert-manager + Let’s Encrypt sur l’ingress NGINX est posé une fois pour toutes, à l’identique sur chaque environnement. Pas de gestion de certificats au cas par cas, donc pas de double configuration possible.
  • Le déploiement bloqué en bloc → comme on sait que le périmètre de prod s’applique d’un seul tenant, l’état de réconciliation GitOps est le premier endroit qu’on regarde. Ce qui surprend ceux qui débutent sur K8s est devenu notre réflexe de diagnostic n°1.
  • La chaîne d’image → build et mise à jour déclarés en GitOps (ImageRepository / ImagePolicy / ImageUpdateAutomation), versionnés et revus en merge request. Un correctif poussé suit un chemin connu, automatique, jusqu’au cluster.
  • Les secrets → chiffrés via SOPS dans le dépôt, déchiffrés par Flux au moment de l’application. Aucune variable sensible en clair, jamais — même temporairement.
  • Le reste (support WebP, dimensionnement des volumes, redirections multishop, maintenance héritée) → c’est notre checklist post-migration qui l’attrape, à chaque mise en prod, sans compter sur la mémoire de qui que ce soit.

Un bug qu’on corrige une fois mais qu’on ne rend pas structurellement impossible reviendra. Sur ces migrations, l’objectif n’est pas d’« être prudent » : c’est de rendre chaque piège inopérant par construction.

👉 Autrement dit : ces erreurs restent parfaitement possibles sur une migration menée à la main, dans l’urgence. Sur la nôtre, elles se heurtent à un socle pensé pour les absorber — et à une checklist qui ferme la porte sur ce que l’automatisation ne couvre pas.

Notre avis chez Mintfull

Le fil rouge de toutes ces erreurs, c’est le même faux positif : « le front s’affiche, donc c’est bon ». Or sur une migration Kubernetes, le front est précisément la couche qui ment le mieux. Le navigateur et le reverse proxy masquent les défauts de la pile PHP, les volumes masquent les fichiers de l’image, les jobs de synchro ou de déploiement « tout verts » masquent des actions qui n’ont jamais eu lieu.

Migrer un PrestaShop sur K8s, ce n’est pas porter du code, c’est reconstruire un environnement d’exécution. La plupart des bugs de migration ne sont pas des bugs de code : ce sont des écarts entre l’environnement de départ et celui d’arrivée. On les trouve en vérifiant l’environnement, pas en relisant l’application.

Notre méthode tient en une phrase : ne jamais se fier à la partie visible, et traiter chaque incident de migration comme une vérification à ajouter à la checklist. Une migration réussie, ce n’est pas une migration sans surprise — c’est une migration où chaque surprise a été transformée en point de contrôle réutilisable pour la suivante. C’est la même rigueur qu’on applique quand on prépare une montée vers PrestaShop 9 : anticiper les impacts d’environnement avant de toucher au code.

FAQ : migrer un PrestaShop sur Kubernetes

Pourquoi le front fonctionne-t-il alors que le back-office plante après une migration K8s ?

Parce qu’ils ne sollicitent pas la même chaîne technique. Le front est largement servi par le navigateur et le reverse proxy (images, fichiers statiques), qui n’ont pas besoin de PHP pour la plupart des assets. Le back-office, les PDF et les traitements d’image, eux, dépendent de la pile PHP réelle du conteneur. Une extension mal compilée (GD sans WebP, par exemple) casse le second sans affecter le premier.

Pourquoi un module JavaScript cesse-t-il de fonctionner sans aucune erreur 404 ?

Souvent parce qu’un volume persistant a été monté par-dessus un dossier qui contenait des fichiers statiques livrés dans l’image Docker, masquant ces fichiers. PrestaShop teste l’existence du fichier avant d’émettre la balise <script> ; si le fichier est masqué, la balise n’est jamais émise. Il n’y a donc pas de 404, juste une fonctionnalité disparue. La solution est de réalimenter le volume avec les assets concernés.

Comment vérifier que mon image PHP supporte bien le WebP ?

Sur le conteneur PHP du cluster, exécutez php -r 'var_dump(gd_info()["WebP Support"]);'. Le résultat doit être bool(true). Si c’est false et que votre catalogue contient des images WebP (y compris renommées en .jpg), vous aurez des fatales en back-office, en génération de PDF et lors de la régénération de miniatures, alors même que le front s’affiche normalement.

Pourquoi mon déploiement GitOps ne fait-il « rien » alors que mes manifestes sont corrects ?

Parce que la plupart des outils GitOps appliquent un lot de ressources comme une unité : si une seule ressource du périmètre est invalide, le lot entier est refusé, y compris vos ressources saines. Vérifiez l’état de réconciliation global du périmètre de production (qu’il soit Ready) avant d’investiguer votre application. Une ressource invalide sans rapport peut bloquer toute la mise en prod.

Faut-il gérer les certificats TLS dans le cluster ?

Le plus souvent, oui : la solution standard est cert-manager dans le cluster, qui délivre des certificats Let’s Encrypt automatiquement via l’ingress NGINX (un ClusterIssuer + l’annotation cert-manager.io/cluster-issuer sur l’ingress). C’est ce qu’on déploie par défaut. L’exception, ce sont les plateformes managées où le load balancer termine déjà le TLS en amont : l’y gérer aussi dans le cluster devient alors redondant ou conflictuel. La bonne démarche est donc de vérifier qui termine réellement le TLS avant de configurer, puis d’adapter le DNS en conséquence (CNAME vers le load balancer, mais ALIAS / enregistrement A à l’apex du domaine, où le CNAME est interdit).


Vous préparez la migration d’un PrestaShop vers Kubernetes, ou vous venez d’en faire une qui « marche presque » et vous voulez en avoir le cœur net ?

👉 Notre agence PrestaShop mène ces migrations de bout en bout : préparation de l’image et de l’environnement, déploiement GitOps maîtrisé, et surtout une checklist de vérifications post-migration qui attrape les bugs silencieux avant vos clients. Parce qu’une migration réussie ne se mesure pas à « le site répond », mais à « tout fonctionne, y compris ce qu’on ne voit pas ».

Une migration bien préparée, c’est une mise en prod sans mauvaise surprise — et une boutique qui tourne pour de vrai, pas seulement en façade.

Nous vous recommandons aussi