4 minutes et 10 secondes, systématiquement, pour reconstruire une image Docker après la moindre modification d’un fichier PHP du thème — alors que ni les dépendances Composer, ni les paquets système, ni rien d’autre n’avait changé. Ce temps de reconstruction, mesuré sur la pipeline de déploiement continu d’un projet WordPress conteneurisé, s’est réduit à 18 secondes après une simple réorganisation des instructions du Dockerfile.
Docker construit une image couche par couche, chaque instruction du Dockerfile produisant une couche mise en cache. Tant que le contenu d’une instruction et de celles qui la précèdent reste identique, Docker réutilise la couche en cache plutôt que de la reconstruire. Mais dès qu’une instruction change, cette couche et toutes celles qui la suivent doivent être reconstruites, même si leur contenu propre n’a pas changé.
Le Dockerfile fautif, ligne par ligne
Le Dockerfile initial copiait l’intégralité du code source du projet avant d’installer les dépendances Composer :
FROM php:8.2-fpm
COPY . /var/www/html
WORKDIR /var/www/html
RUN composer install --no-dev --optimize-autoloader
Avec cet ordre, toute modification d’un fichier du projet — même un simple fichier PHP du thème, sans rapport avec les dépendances — invalide la couche COPY . /var/www/html, et donc également la couche RUN composer install qui la suit, puisque Docker ne peut plus garantir que le contenu sur lequel composer install s’exécute est identique à la dernière construction. L’installation complète des dépendances, y compris le téléchargement des paquets non déjà présents localement, se relance à chaque fois.
La réorganisation qui change tout
La correction sépare la copie des fichiers de dépendances de la copie du reste du code source :
FROM php:8.2-fpm
WORKDIR /var/www/html
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts --no-autoloader
COPY . .
RUN composer dump-autoload --optimize

Avec cet ordre, la couche RUN composer install ne dépend plus que du contenu de composer.json et composer.lock. Tant que ces deux fichiers ne changent pas — ce qui est le cas pour la grande majorité des modifications quotidiennes portant sur le code applicatif — Docker réutilise la couche en cache et saute entièrement l’installation des dépendances, ne reconstruisant que la copie du code source et la régénération de l’autoloader, une opération nettement plus rapide.
Mesurer l’impact réellement
La mesure a été effectuée en modifiant un unique fichier PHP du thème puis en relançant une construction complète de l’image, dans les mêmes conditions matérielles, avant et après réorganisation :
| Étape | Avant réorganisation | Après réorganisation |
|---|---|---|
| Copie des fichiers | 2 s | 2 s |
| Installation Composer | 3 min 55 s | 0 s (couche en cache) |
| Régénération autoloader | inclus ci-dessus | 16 s |
| Total | 4 min 10 s | 18 s |
Pourquoi ce gain compte sur une pipeline de déploiement continu
Sur un projet où chaque poussée de code déclenche une reconstruction de l’image dans la chaîne d’intégration continue, ce gain se répercute directement sur le temps d’attente avant chaque déploiement. À raison d’une dizaine de constructions par jour ouvré sur ce projet, l’économie cumulée représentait plus d’une demi-heure d’attente évitée quotidiennement pour l’équipe, sans compter l’économie de ressources de calcul sur les serveurs d’intégration continue.
Le même principe s’applique aux dépendances front-end
Le même raisonnement vaut pour un package.json et les dépendances Node.js d’un thème à blocs : copier package.json et le fichier de verrouillage avant le reste du code, installer les dépendances, puis copier le code source restant, préserve le cache de la même façon pour les modifications qui ne touchent pas aux dépendances front-end.
Ce qui reste à surveiller malgré tout
Cette réorganisation optimise le cas le plus fréquent — une modification du code sans changement de dépendances — mais toute modification de composer.json ou composer.lock continue légitimement d’invalider la couche d’installation, comme il se doit. Le gain de temps ne dispense pas non plus de choisir une image de base adaptée et de limiter le nombre de couches inutiles ailleurs dans le fichier.
En résumé
Réordonner les instructions d’un Dockerfile pour isoler les fichiers de dépendances du reste du code source ne demande aucune modification du code applicatif lui-même, seulement une meilleure compréhension du mécanisme de cache par couches de Docker. Le gain mesuré ici, d’un facteur supérieur à dix sur le temps de reconstruction, illustre à quel point cet ordre, souvent négligé lors de la première écriture d’un Dockerfile, mérite d’être revu systématiquement.