← Retour aux tutoriels

Corriger l’erreur Docker Build « failed to compute cache key »

dockerbuildkitdockerfilecachebackend

Corriger l’erreur Docker Build « failed to compute cache key »

L’erreur Docker Build « failed to compute cache key » apparaît quand Docker (souvent via BuildKit) n’arrive pas à calculer une clé de cache pour une étape de build. Dit autrement : Docker tente de déterminer si une étape peut être réutilisée depuis le cache (pour accélérer le build), mais une des entrées nécessaires (fichiers du contexte, métadonnées, permissions, liens symboliques, accès à un chemin, etc.) est inaccessible, incohérente, ou non déterministe.

Ce tutoriel explique en profondeur :


1) Comprendre ce que Docker essaie de faire (BuildKit et cache key)

1.1 BuildKit calcule une clé à partir des entrées d’une étape

Avec BuildKit (activé par défaut sur beaucoup d’installations modernes), chaque instruction du Dockerfile (ex. COPY, RUN, ADD) est transformée en une opération. Pour décider si l’opération peut être récupérée depuis le cache, BuildKit calcule une clé basée sur :

Si BuildKit ne peut pas lire un fichier du contexte, ou si un chemin mentionné n’existe pas, ou si un lien symbolique pointe vers l’extérieur, le calcul échoue et vous obtenez :

1.2 Pourquoi l’erreur apparaît parfois “au hasard”

Le cache dépend des fichiers réellement envoyés au daemon (le contexte). Si votre contexte change (fichier généré, symlink, permission), ou si votre .dockerignore exclut un fichier nécessaire, l’erreur peut apparaître selon la machine, le répertoire courant, ou l’ordre de génération des artefacts.


2) Reproduire et lire correctement l’erreur

2.1 Activer une sortie plus lisible (progress=plain)

Utilisez BuildKit et demandez une sortie “plain” :

DOCKER_BUILDKIT=1 docker build --progress=plain -t monimage:debug .

Si vous utilisez docker buildx :

docker buildx build --progress=plain -t monimage:debug .

2.2 Exemple typique

Vous pourriez voir quelque chose comme :

#7 [stage-0 3/6] COPY package.json package-lock.json ./
#7 ERROR: failed to compute cache key: failed to calculate checksum of ref ...: "/package-lock.json": not found

Ici, BuildKit veut checksummer package-lock.json (pour la clé de cache de COPY), mais il n’est pas présent dans le contexte (ou il est ignoré, ou le chemin est faux).


3) Cause n°1 : fichier manquant dans le contexte (chemin faux ou build lancé au mauvais endroit)

3.1 Vérifier le répertoire courant

Beaucoup de builds échouent simplement parce qu’on exécute docker build depuis le mauvais dossier.

Dans ce cas, le contexte . ne contient pas vos sources.

Solution : définir explicitement -f et le contexte :

docker build -f docker/Dockerfile -t monimage:latest .

Ici, le contexte est . (racine du projet), et le Dockerfile est ailleurs.

3.2 Vérifier les chemins dans COPY

Dans un Dockerfile, COPY lit depuis le contexte, pas depuis le système de fichiers de l’hôte au sens large.

Mauvais exemple :

COPY ../monfichier.txt /app/

BuildKit interdit généralement de sortir du contexte.

Correct : ajustez le contexte ou déplacez le fichier dans le contexte.

Si vous avez besoin d’un contexte différent :

docker build -f docker/Dockerfile -t monimage:latest ..

Mais attention : élargir le contexte peut envoyer énormément de fichiers (lent, fuite de secrets).


4) Cause n°2 : .dockerignore exclut un fichier nécessaire

4.1 Symptôme

Vous avez bien le fichier dans votre projet, mais Docker dit not found lors d’un COPY. Souvent c’est .dockerignore.

Exemple de .dockerignore trop agressif :

**/*.json
node_modules
dist

Puis votre Dockerfile :

COPY package.json package-lock.json ./

BuildKit ne voit pas ces fichiers, donc checksum impossible => failed to compute cache key.

4.2 Diagnostiquer : afficher le contexte réellement envoyé

Docker ne fournit pas directement “la liste finale”, mais vous pouvez :

  1. Vérifier la taille du contexte dans la sortie build (souvent affichée).
  2. Faire un test en construisant un tar du contexte (approche pratique).

Sur Linux/macOS, vous pouvez simuler l’archive de contexte avec tar en respectant .dockerignore est non trivial. Une méthode plus simple : temporairement renommer .dockerignore :

mv .dockerignore .dockerignore.bak
DOCKER_BUILDKIT=1 docker build --progress=plain -t test:noignore .
mv .dockerignore.bak .dockerignore

Si ça marche sans .dockerignore, vous avez trouvé.

4.3 Corriger proprement

Ajustez .dockerignore pour inclure explicitement ce qu’il faut. Par exemple :

**/*.json
!package.json
!package-lock.json
node_modules
dist

Ou mieux : évitez les patterns trop globaux.


5.1 Pourquoi ça casse la clé de cache

BuildKit “marche” le répertoire pour calculer des checksums. Un symlink :

peut faire échouer le “walk” ou le calcul de checksum.

Sur Linux/macOS :

find . -type l -print

Trouver les symlinks cassés :

find . -type l ! -exec test -e {} \; -print

Voir où pointe un lien :

readlink -f chemin/du/lien  # Linux
readlink chemin/du/lien     # macOS (sans -f selon version)

5.3 Solutions


6) Cause n°4 : permissions et fichiers illisibles (EACCES)

6.1 Symptôme

Vous voyez permission denied pendant le build, parfois enveloppé dans failed to compute cache key.

Exemples :

6.2 Diagnostiquer

Lister les permissions :

ls -la

Trouver des fichiers non lisibles :

find . -type f ! -readable -print

Trouver des dossiers non traversables :

find . -type d ! -executable -print

6.3 Corriger

Rendre lisible (selon politique de sécurité) :

chmod -R u+rwX,go+rX .

Ou corriger propriétaire :

sudo chown -R "$USER":"$USER" .

Attention : ne faites pas ça aveuglément dans des répertoires sensibles. Ciblez le sous-répertoire du projet.


7) Cause n°5 : chemins “générés” attendus mais non créés (build non déterministe)

7.1 Exemple courant : dist/ absent

Dockerfile :

COPY dist/ /app/dist/

Mais dist/ est généré par un build local et n’existe pas sur une machine fraîche (CI).

Résultat : checksum impossible => erreur.

7.2 Approches correctes

Option A : générer dist/ dans l’image (recommandé)

Exemple Node.js multi-étapes :

FROM node:20-alpine AS build
WORKDIR /src
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=build /src/dist ./dist
COPY --from=build /src/package.json ./package.json
CMD ["node", "dist/index.js"]

Ici, vous ne dépendez pas d’un artefact local.

Option B : s’assurer que dist/ existe avant le build

Dans un pipeline CI :

npm ci
npm run build
docker build -t monimage:latest .

Mais attention : vous envoyez potentiellement beaucoup de fichiers (et vous devez gérer .dockerignore).


8) Cause n°6 : confusion entre ADD et COPY, archives, URLs

ADD a des comportements supplémentaires (décompression d’archives locales, téléchargement d’URL). Cela peut introduire des effets de bord et compliquer le cache.

8.1 Recommandation

Exemple robuste :

RUN apk add --no-cache curl ca-certificates \
 && curl -fsSL -o /tmp/out.tar.gz https://example.com/out-1.2.3.tar.gz \
 && echo "abc123...  /tmp/out.tar.gz" | sha256sum -c - \
 && tar -xzf /tmp/out.tar.gz -C /opt \
 && rm -f /tmp/out.tar.gz

9) Cause n°7 : Build context énorme, fichiers spéciaux, sockets, devices

Si vous faites docker build . à la racine de votre home ou d’un monorepo mal ignoré, vous pouvez inclure :

BuildKit peut échouer en essayant de lire des entrées non régulières.

9.1 Corriger : réduire le contexte avec .dockerignore

Exemple solide pour un projet Node :

.git
node_modules
dist
coverage
.DS_Store
*.log
.env
.env.*

Et dans le Dockerfile, copiez d’abord les manifests pour maximiser le cache :

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

10) Cause n°8 : erreurs dans un build multi-plateforme (buildx) et chemins relatifs

En build multi-plateforme (docker buildx build --platform linux/amd64,linux/arm64 ...), BuildKit est plus strict et l’environnement diffère.

10.1 Diagnostic

Affichez le builder et sa config :

docker buildx ls
docker buildx inspect --bootstrap

Essayez un build mono-plateforme pour isoler :

docker buildx build --platform linux/amd64 --progress=plain -t monimage:test .

10.2 Causes fréquentes

Même si l’erreur mentionne le cache key, la source peut être un fichier introuvable lors d’un COPY conditionnel (ou un COPY qui dépend d’un argument).


11) Méthode de diagnostic systématique (checklist)

Quand vous voyez failed to compute cache key, appliquez cette procédure :

11.1 Étape 1 : localiser l’instruction fautive

Relancez avec :

DOCKER_BUILDKIT=1 docker build --progress=plain --no-cache -t debug:no-cache .

Même si --no-cache désactive la réutilisation, BuildKit calcule encore des checksums pour COPY. La sortie “plain” vous dira quelle étape échoue.

11.2 Étape 2 : vérifier l’existence du chemin côté hôte (dans le contexte)

Si l’erreur mentionne "/chemin": not found, vérifiez :

ls -la chemin

Si c’est un fichier :

test -f chemin && echo OK || echo MANQUANT

Si c’est un dossier :

test -d chemin && echo OK || echo MANQUANT

11.3 Étape 3 : vérifier .dockerignore

Cherchez des patterns qui matchent :

cat .dockerignore

Test rapide : renommer .dockerignore temporairement (voir section 4.2).

Symlinks cassés :

find . -type l ! -exec test -e {} \; -print

Fichiers non lisibles :

find . -type f ! -readable -print

11.5 Étape 5 : contexte correct et Dockerfile correct

Vérifiez la commande :

Exemple correct :

docker build -f docker/Dockerfile -t monimage:latest .

12) Corrections “patterns” avec exemples concrets

12.1 Pattern : COPY de fichiers optionnels

Docker ne gère pas “copier si existe” nativement. Si vous écrivez :

COPY package-lock.json ./

et que parfois vous n’avez pas de lockfile, ça casse.

Solutions :

Exemple pnpm :

COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile

12.2 Pattern : mauvais chemin de WORKDIR

Si vous faites :

WORKDIR /app
COPY . .
RUN cat ./scripts/build.sh

Mais scripts/build.sh est ignoré par .dockerignore, ou n’existe pas, l’erreur peut se produire au COPY ou plus tard.

Bon réflexe : copiez explicitement ce qui est nécessaire, par couches :

WORKDIR /app
COPY scripts/ ./scripts/
COPY src/ ./src/
COPY package.json package-lock.json ./

Cela rend aussi le cache plus stable.

12.3 Pattern : fichiers générés par Git (submodules) non initialisés

Si votre Dockerfile copie un sous-répertoire qui est un submodule Git, mais que le submodule n’est pas initialisé sur la machine/CI :

COPY vendor/ ./vendor/

Vous aurez “not found”.

Solution CI :

git submodule update --init --recursive
docker build -t monimage:latest .

13) Nettoyer et repartir sur une base saine (cache, builder, etc.)

Parfois, vous avez corrigé le contexte mais un builder BuildKit a un état incohérent (rare, mais possible). Vous pouvez nettoyer.

13.1 Nettoyer le cache BuildKit (prune)

docker builder prune

Plus agressif :

docker builder prune --all --force

Avec buildx :

docker buildx prune --all --force

13.2 Nettoyer images/containers inutilisés (attention)

docker system prune

Plus agressif (supprime aussi images non utilisées) :

docker system prune -a

14) Bonnes pratiques pour éviter l’erreur à l’avenir

14.1 Garder un contexte minimal

14.2 Rendre les chemins explicites et stables

14.3 Préférer les builds multi-étapes

Ils évitent de dépendre d’artefacts locaux. Exemple générique :

FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/app ./cmd/app

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/app /app
USER 65532:65532
ENTRYPOINT ["/app"]

Si vous utilisez des symlinks pour du dev local, envisagez :


15) Cas pratiques (diagnostic + correction)

15.1 Cas : COPY requirements.txt échoue en Python

Erreur :

failed to compute cache key: failed to calculate checksum ... "/requirements.txt": not found

Diagnostic :

ls -la requirements.txt
cat .dockerignore
pwd

Corrections possibles :

Dockerfile conseillé :

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

15.2 Cas : COPY . . échoue à cause d’un socket

Si votre projet contient un fichier spécial (ex. docker.sock ou socket de dev), BuildKit peut échouer.

Diagnostic :

find . -type s -print
find . -type p -print

Correction : ignorez-les :

**/*.sock

ou mieux, ignorez le répertoire qui les contient.


16) Résumé rapide des causes et remèdes


17) Commandes utiles (pense-bête)

# Build verbeux
DOCKER_BUILDKIT=1 docker build --progress=plain -t monimage:debug .

# Sans cache (pour isoler)
DOCKER_BUILDKIT=1 docker build --progress=plain --no-cache -t monimage:nocache .

# Lister symlinks
find . -type l -print

# Symlinks cassés
find . -type l ! -exec test -e {} \; -print

# Fichiers non lisibles
find . -type f ! -readable -print

# Dossiers non traversables
find . -type d ! -executable -print

# Nettoyage cache build
docker builder prune --all --force
docker buildx prune --all --force

Si vous collez ici :

  1. la ligne exacte de l’erreur (les 20–30 lignes autour),
  2. votre commande docker build ...,
  3. les extraits pertinents du Dockerfile et .dockerignore,

je peux vous dire précisément quelle entrée empêche le calcul de la cache key et proposer la correction la plus propre.