Chaque instruction du Dockerfile (FROM, RUN, COPY, …) produit une couche. Au build suivant, Docker compare l’instruction et ses entrées à ce qu’il a déjà : si rien n’a changé, il réutilise la couche ; si quelque chose a changé, il invalide cette couche et toutes celles d’après.
L’ordre n’est donc pas une affaire de style. Il décide ce qui est recalculé à chaque commit.
# Les dépendances bougent rarement : on les copie d’abord.
COPY package.json package-lock.json ./
RUN npm ci
# Le code bouge à chaque commit : on le copie ensuite.
COPY src ./srcSi vous inversez — COPY . . puis RUN npm ci — chaque modification d’un fichier source casse le cache de l’installation. Vous retéléchargez internet pour avoir changé une virgule.
Que vous connaissez la règle d’invalidation, pas seulement « le cache accélère le build ». La phrase attendue : la première instruction dont l’entrée change jette le reste.
Deux précisions qui séparent une réponse moyenne d’une bonne :
COPY et ADD hashent le contenu. Ce n’est pas le texte de la ligne qui compte, c’est le fichier. Un COPY src ./src est un miss dès qu’un fichier de src/ change, même si la ligne du Dockerfile est identique.RUN trop large (apt-get update && npm ci && npm run build dans la même couche) est un cache tout-ou-rien. Parfois c’est voulu (nettoyer les listes apt dans la même couche) ; parfois vous avez collé deux cycles de vie différents.« Pourquoi mon cache est-il mort alors que je n’ai touché que le README ? »
Parce qu’un COPY . . trop tôt a vu le README. C’est le cas d’école du fichier dockerignore oublié.
« Le cache peut-il être trop agressif ? »
Oui. Une couche RUN npm ci réutilisée alors que le registre a publié un correctif, ou un ARG oublié qui ne fait pas partie de la clé. Le cache n’est pas un oracle : il répète ce qu’il a déjà fait. Le diagnostic de ce cas est une question à part.