
Le 14 août, j’ai remplacé la photo de profil de ce site. Même fichier, même chemin : public/jules.png. Seul le contenu changeait.
Le déploiement récupérait bien le commit. Le build passait. Le processus redémarrait avec pm2. Pourtant, Next.js pouvait encore servir l’ancienne photo.
Le fichier présent sur le serveur n’était pas le problème. C’était la version optimisée produite avant son remplacement.
## Un déploiement qui ne repart pas vraiment de zéro
Mon script suit une séquence assez simple : il récupère le code, construit l’application, puis redémarre le processus. J’avais implicitement rangé npm run build dans la catégorie des opérations qui remettent l’état de l’application à plat.
Ce n’est pas ce qu’il faisait pour les images.
Le composant `next/image` ne renvoie pas forcément directement le fichier placé dans public. Il génère des variantes adaptées à la taille et au format demandés, puis les écrit dans un cache sur disque pour éviter de recommencer le travail à chaque requête.
Dans mon cas, ce cache se trouvait dans .next/cache/images. Il survivait au nouveau build. Il survivait aussi au redémarrage pm2, puisque redémarrer un processus ne supprime pas les fichiers déjà présents sur le VPS.
La nouvelle photo était donc bien déployée, mais une ancienne variante optimisée restait disponible au même endroit. Comme l’URL utilisée par le site n’avait pas changé, le déploiement ne forçait pas la régénération que j’attendais.
C’est un cas où chaque étape réussit séparément tout en produisant un résultat faux. Git avait le bon fichier. Le build terminait correctement. pm2 relançait bien l’application. Aucun de ces signaux ne disait que le cache d’images contenait encore une version devenue obsolète.
## J’ai choisi une purge ciblée
La documentation de Next.js sur le cache des images est assez directe sur ce point : il n’existe pas de mécanisme général d’invalidation. Pour remplacer immédiatement une image, il faut notamment changer sa source ou supprimer le cache situé dans <distDir>/cache/images.
J’avais donc plusieurs choix.
- Renommer la photo à chaque modification.
- Ajouter une version dans son URL.
- Passer par un import statique avec un nom généré à partir du contenu.
- Supprimer les variantes optimisées pendant le déploiement.
J’ai retenu la dernière option.
Je voulais garder un chemin stable pour cette image. Je ne voulais pas non plus ajouter un mécanisme de version uniquement pour une photo de profil modifiée occasionnellement. Mon script de déploiement purge maintenant .next/cache/images avant de relancer l’application.
La purge reste volontairement limitée au cache des images. Je ne supprime pas tout .next sans distinction après le build. Le problème identifié est précis, donc la correction l’est aussi.
Le coût est simple : après chaque déploiement, les premières requêtes doivent régénérer les variantes nécessaires. Sur ce portfolio, je préfère ce petit travail prévisible à la possibilité de conserver une image périmée pendant une durée difficile à voir depuis le pipeline.
## Ce que ce bug change dans ma manière de voir le VPS
Sur une plateforme éphémère, on s’attend facilement à ce qu’un nouveau déploiement arrive dans un environnement neuf. Mon portfolio tourne sur mon propre serveur. Son disque est persistant, et c’est normalement un avantage.
Mais cette persistance concerne aussi les fichiers que je ne pense pas à nettoyer.
Le guide officiel sur l’auto-hébergement de Next.js rappelle que plusieurs caches sont conservés sur le système de fichiers local. Mon script ne doit donc pas seulement installer la nouvelle version du code. Il doit aussi décider quels états de l’ancienne version restent valides.
Depuis le correctif, remplacer une image sous le même nom ne dépend plus d’un cache créé lors d’un précédent déploiement. La prochaine modification de public/jules.png suivra le même chemin que le reste du code : le fichier sera récupéré, les anciennes variantes seront supprimées, puis Next.js les reconstruira à partir de la bonne source.
Je garde cette purge dans le script pour l’instant. Si les images deviennent plus nombreuses ou si leur régénération commence à peser, je passerai probablement les fichiers réellement immuables vers des imports statiques avec des noms hashés. Pour la photo de profil actuelle, supprimer ce cache à chaque déploiement reste la solution la plus lisible.