← Terug naar tutorials

Docker build error oplossen: ‘failed to compute cache key’ (backend)

dockerbuildkitci/cdbackenddebuggingdockerfile

Docker build error oplossen: ‘failed to compute cache key’ (backend)

Deze tutorial helpt je een veelvoorkomende Docker build-fout op te lossen:

failed to compute cache key

Je ziet deze fout vaak bij het bouwen van backend-images (Node.js, Python, Java, Go, .NET), vooral wanneer BuildKit aan staat (wat tegenwoordig standaard is). De melding is frustrerend omdat hij meestal niet direct zegt welk bestand of welke stap het probleem veroorzaakt. In deze gids leer je:

Alles is in Markdown en met echte commando’s die je kunt copy-pasten.


1) Wat betekent “failed to compute cache key” precies?

Docker BuildKit bouwt images in stappen. Elke stap (bijv. RUN, COPY, ADD) krijgt een cache key: een soort hash die afhangt van:

Als BuildKit een stap wil uitvoeren, probeert het eerst te bepalen of er een cached resultaat bestaat. Om dat te doen moet het de cache key kunnen berekenen. Als BuildKit daarbij niet bij bestanden kan, of een bestand niet kan “snapshotten” (vastleggen), of er inconsistenties zijn, dan krijg je:

Belangrijk: de fout treedt vaak op bij COPY/ADD (of bij een RUN dat bestanden verwacht), omdat BuildKit dan de checksums van de bronbestanden wil berekenen.


2) Snelle triage: waar gaat het mis?

2.1 Zet BuildKit output op “plain” voor meer details

BuildKit geeft soms te weinig context in de standaard UI. Gebruik:

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

Of als je docker buildx gebruikt:

docker buildx build --progress=plain -t mijn-backend:debug .

Zo zie je per stap welke bestanden worden gebruikt.

2.2 Zoek de stap die faalt

In de output zie je iets als:

#8 [stage-0 4/9] COPY package.json package-lock.json ./
#8 ERROR: failed to compute cache key: failed to calculate checksum of ref ...

De stap (COPY ...) is je startpunt. De bronpaden in die COPY zijn vaak de oorzaak.

2.3 Controleer je build context

De build context is de map die je als laatste argument meegeeft (meestal .). Docker stuurt die map (minus .dockerignore) naar de builder. Als je per ongeluk de verkeerde context bouwt, ontbreken bestanden.

Controleer waar je bent:

pwd
ls -la

En bouw expliciet vanuit de juiste map:

docker build -t mijn-backend:debug /pad/naar/project

3) Meest voorkomende oorzaken + oplossingen

Oorzaak A: Bestand bestaat niet in de build context (of wordt genegeerd)

Symptomen

Controle

Open .dockerignore:

cat .dockerignore

Zoek naar regels die per ongeluk belangrijke bestanden uitsluiten, zoals:

Test of een bestand in de context zit door een tijdelijke build stap:

# debug
FROM alpine:3.20
WORKDIR /src
COPY . .
RUN ls -la && find . -maxdepth 2 -type f | sed -n '1,200p'

Build:

docker build --no-cache --progress=plain -t context-debug .

Oplossing

Voor Node.js backend is dit vaak een goede basis:

COPY package.json package-lock.json ./
RUN npm ci
COPY src ./src

In plaats van:

COPY . .
RUN npm install

Oorzaak B: COPY verwijst naar een glob/patroon dat soms niets matcht

Docker COPY ondersteunt beperkte wildcarding. Als je iets doet als:

COPY package*.json ./

en er bestaat alleen package.json maar geen package-lock.json, dan kan dit afhankelijk van Docker/BuildKit-versie of context rare errors geven (meestal “no source files were specified”, soms indirect “cache key” issues).

Controle

Bekijk welke files er echt zijn:

ls -la package*.json

Oplossing

Maak het expliciet of zorg dat het lockfile altijd aanwezig is.

Expliciet:

COPY package.json ./
# Alleen als je zeker weet dat package-lock.json bestaat:
COPY package-lock.json ./

Of: zorg dat je lockfile committed is (aanbevolen voor reproduceerbare builds).


BuildKit probeert bestanden te “walken”. Als je symlink in de context verwijst naar een pad buiten de context, kan BuildKit dat niet meenemen. Dit is heel typisch bij:

find . -maxdepth 4 -type l -ls

Bekijk waar ze naartoe wijzen:

readlink -f pad/naar/symlink

Als de target buiten je projectmap ligt, is dat verdacht.

Oplossingen

  1. Kopieer geen symlink-structuren; kopieer broncode en installeer dependencies in de container.
  2. Gebruik een monorepo-strategie: bouw vanuit de repo-root en zet de context op root.
  3. Vermijd COPY . . als dat symlinks meeneemt die naar buiten wijzen.
  4. Voor pnpm: gebruik een Dockerfile die pnpm correct gebruikt en geen host node_modules kopieert.

Voorbeeld (Node + pnpm) robuust:

FROM node:20-alpine AS deps
WORKDIR /app
RUN corepack enable

COPY package.json pnpm-lock.yaml ./
RUN pnpm fetch

FROM node:20-alpine AS build
WORKDIR /app
RUN corepack enable

COPY --from=deps /root/.local/share/pnpm/store /root/.local/share/pnpm/store
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --offline

COPY src ./src
RUN pnpm run build

Let op: kopieer geen node_modules vanaf je host.


Oorzaak D: Bestandspermissies of onleesbare bestanden in de context

Soms zit er een bestand in je context dat Docker niet kan lezen (permissions) of waar BuildKit bij het checksum-berekenen op faalt.

Controle

Zoek bestanden zonder leesrechten:

find . -type f ! -readable -ls | sed -n '1,200p'

Of check vreemde permissions:

find . -maxdepth 3 -type f -perm -0002 -ls | sed -n '1,200p'

Oplossing

chmod -R u+rwX,go+rX .

Aanbevolen .dockerignore voor backend-projecten:

.git
node_modules
dist
build
coverage
tmp
.cache
.DS_Store
*.log
.env
.env.*

Pas dit aan op jouw stack.


Oorzaak E: Case-sensitivity problemen (macOS/Windows vs Linux)

Op macOS/Windows is het bestandssysteem vaak case-insensitive. In Linux (in de container) is het case-sensitive. Dit kan indirect tot cache key issues leiden wanneer paden niet consistent zijn.

Voorbeeld:

Controle

Check de echte namen:

ls -la
find . -maxdepth 2 -type d -print

Oplossing

git mv src src_tmp
git mv src_tmp src

Oorzaak F: Git submodules of gegenereerde bestanden ontbreken

Als je Dockerfile iets kopieert dat lokaal bestaat maar in CI ontbreekt (submodule niet gecheckt, build artifacts niet gegenereerd), dan faalt de checksum.

Controle (submodules)

git submodule status

Initialiseer in CI of lokaal:

git submodule update --init --recursive

Oplossing


Oorzaak G: Je gebruikt ADD met remote URL of archief op een manier die BuildKit niet kan cachen

ADD kan archieven uitpakken en remote URLs downloaden. Dit kan caching complex maken.

Oplossing

Voorbeeld:

RUN apk add --no-cache curl \
 && curl -fsSL -o tool.tgz https://example.com/tool.tgz \
 && echo "expected_sha256  tool.tgz" | sha256sum -c - \
 && tar -xzf tool.tgz -C /usr/local/bin

4) Systematisch debuggen: een stappenplan dat bijna altijd werkt

Stap 1: Bouw zonder cache en met plain output

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

Als het dan nog faalt, is het geen “oude cache” maar een echt context/pad probleem.

Stap 2: Minimaliseer je Dockerfile tot de failing stap

Comment tijdelijk alles weg behalve FROM, WORKDIR, en de failing COPY.

Voorbeeld:

FROM alpine:3.20
WORKDIR /app
COPY package.json package-lock.json ./
RUN ls -la

Build opnieuw. Als dit faalt, weet je zeker dat het om die bestanden gaat.

Stap 3: Inspecteer .dockerignore en de context

Toon de bestanden die je verwacht:

ls -la package.json package-lock.json

Als één ontbreekt: fix dat.

find . -type l -ls | sed -n '1,200p'
find . -type f ! -readable -ls | sed -n '1,200p'

Stap 5: Probeer een “context listing” in Docker build

Gebruik een debug stage:

FROM alpine:3.20
WORKDIR /ctx
COPY . .
RUN echo "Top-level:" && ls -la \
 && echo "Zoek package files:" && find . -maxdepth 3 -name "package*.json" -print

Als je file daar niet verschijnt: .dockerignore of verkeerde context.


5) Veelvoorkomende backend-scenario’s (met concrete fixes)

5.1 Node.js (npm) backend: lockfile ontbreekt of wordt genegeerd

Probleem: Dockerfile:

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

Maar package-lock.json ontbreekt of staat in .dockerignore.

Fix:

COPY package.json ./
RUN npm install

Maar let op: npm install zonder lockfile is minder reproduceerbaar. Voor CI/production is npm ci met lockfile beter.

Aanbevolen Dockerfile (Node backend, multi-stage):

FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY package.json ./
COPY src ./src
EXPOSE 3000
CMD ["node", "src/index.js"]

Pas src/index.js aan naar jouw entrypoint.


5.2 Python backend: COPY requirements.txt faalt door verkeerde context

Probleem: Je draait docker build vanuit backend/, maar je Dockerfile verwacht bestanden uit repo-root (of andersom).

Fix: Bouw met juiste context en Dockerfile pad:

docker build -f backend/Dockerfile -t mijn-api:debug .

Of als je context backend/ moet zijn:

docker build -t mijn-api:debug backend/

Robuuste Python Dockerfile:

FROM python:3.12-slim AS runtime
WORKDIR /app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY app ./app
CMD ["python", "-m", "app"]

5.3 Java (Maven/Gradle): build artifacts kopiëren die niet bestaan

Probleem: Dockerfile:

COPY target/app.jar /app/app.jar

Maar target/app.jar bestaat niet in een clean checkout of in CI.

Fix: Bouw de jar in Docker (multi-stage):

FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /src
COPY pom.xml ./
COPY src ./src
RUN mvn -q -DskipTests package

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /src/target/*.jar app.jar
CMD ["java", "-jar", "app.jar"]

Nu is de context alleen broncode; geen afhankelijkheid van lokale target/.


5.4 .NET: COPY van csproj met wildcards of verkeerde map

Probleem: COPY *.csproj ./ werkt niet als je meerdere projecten hebt of als de csproj in submap staat.

Fix: Maak paden expliciet:

COPY MyApi/MyApi.csproj MyApi/
RUN dotnet restore MyApi/MyApi.csproj
COPY . .
RUN dotnet publish MyApi/MyApi.csproj -c Release -o /out

6) Cache en BuildKit: hoe voorkom je dat dit terugkomt?

6.1 Zet je COPY-volgorde slim

Doel: dependency-install stap cachen zolang lockfiles niet wijzigen.

Node:

COPY package.json package-lock.json ./
RUN npm ci
COPY src ./src

Python:

COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY app ./app

Java: Kopieer eerst pom.xml en run mvn dependency:go-offline of mvn package afhankelijk van je setup.

6.2 Houd je context klein

Hoe groter de context, hoe groter de kans op rare files (caches, sockets, symlinks, secrets). Gebruik .dockerignore agressief.

Controleer context-grootte door te kijken naar de build output: Docker toont vaak “transferring context: X MB”.

6.3 Vermijd het kopiëren van lokale dependency-mappen

Zet ze in .dockerignore.


7) Geavanceerde diagnose: builder state resetten

Soms is de builder-cache corrupt of zit je buildx builder in een vreemde toestand.

7.1 Prune BuildKit caches

Let op: dit kan veel ruimte vrijmaken, maar je verliest caches.

docker builder prune -a

Of specifieker (met buildx):

docker buildx prune -a

7.2 Verwijder en maak een nieuwe buildx builder

docker buildx ls
docker buildx rm mijnbuilder
docker buildx create --name mijnbuilder --use
docker buildx inspect --bootstrap

7.3 Docker Desktop: herstart en reset (laatste redmiddel)

Als je op Docker Desktop zit en niets helpt:


8) Checklist: quickest wins

Loop deze lijst af zodra je failed to compute cache key ziet:

  1. Welke stap faalt?
    Bouw met:

    docker build --progress=plain --no-cache -t debug .
  2. Bestaat elk bronbestand in die COPY echt?

    ls -la pad/naar/bestand
  3. Wordt het bestand uitgesloten door .dockerignore?

    cat .dockerignore
  4. Zijn er symlinks die buiten de context wijzen?

    find . -type l -ls
  5. Bouw je vanuit de juiste map / met de juiste context?

    pwd
  6. Zijn er onleesbare bestanden/permissieproblemen?

    find . -type f ! -readable -ls | head
  7. In CI: ontbreken submodules of gegenereerde artifacts?

    git submodule update --init --recursive

9) Praktisch voorbeeld: fout reproduceren en fixen

Scenario

Je Dockerfile:

FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["node", "src/server.js"]

Je krijgt:

failed to compute cache key: failed to calculate checksum of ref ... "/package-lock.json": not found

Diagnose

  1. Check file:
ls -la package-lock.json

Uitkomst: bestand bestaat niet.

  1. Check .dockerignore:
grep -n "package-lock" -n .dockerignore

Uitkomst: misschien staat er package-lock.json in .dockerignore, of het bestaat gewoon niet.

Fix (beste praktijk)

Genereer en commit lockfile:

npm install
git add package-lock.json
git commit -m "Add package-lock for reproducible Docker builds"

Build opnieuw:

docker build --progress=plain -t mijn-backend:latest .

10) Extra tips voor stabiele backend Docker builds


11) Samenvatting

De fout failed to compute cache key betekent vrijwel altijd dat BuildKit de input van een build stap niet betrouwbaar kan hashen/snapshotten. In backend-projecten komt dat meestal door:

De snelste route naar de oplossing is:

docker build --no-cache --progress=plain -t debug .

en daarna de falende COPY-stap exact nalopen: bestaan de bronpaden, zitten ze in de context, worden ze niet genegeerd, en zijn ze leesbaar?


12) Als je wil: plak je fout + Dockerfile voor een gerichte fix

Als je de exacte foutregel (de volledige BuildKit output rond de failing stap), je Dockerfile en je .dockerignore (of relevante delen) deelt, kan ik heel precies aanwijzen welk pad of bestand de cache key berekening breekt en hoe je Dockerfile het best herstructureert voor jouw backend-stack.