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:
- wat de fout technisch betekent (BuildKit, cache keys, context, snapshots)
- hoe je snel de echte oorzaak vindt met concrete commando’s
- de meest voorkomende oorzaken (COPY/ADD, .dockerignore, symlinks, permissies, ontbrekende files, Git submodules, line endings, case-sensitivity)
- hoe je je Dockerfile robuuster maakt (betere COPY-volgorde, multi-stage, lockfiles, expliciete paden)
- hoe je caching correct gebruikt zonder “spooky” cache errors
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:
- de instructie zelf (bijv.
COPY package.json package-lock.json ./) - de inhoud en metadata van de betrokken bestanden (inhoud, timestamps, permissions, symlinks)
- bepaalde build-args en omgevingsvariabelen
- de base image digest (
FROM node:20-alpine@sha256:...)
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:
failed to compute cache key: failed to calculate checksum of ref ...failed to compute cache key: ... not foundfailed to compute cache key: ... failed to walk ...failed to compute cache key: ... lstat ... no such file or directory
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
COPY failed: file not found in build context or excluded by .dockerignore- of BuildKit-variant:
failed to compute cache key ... not found
Controle
Open .dockerignore:
cat .dockerignore
Zoek naar regels die per ongeluk belangrijke bestanden uitsluiten, zoals:
**/*.json(te breed)node_modules(ok) maar ookpackage*.json(niet ok)distofbuildterwijl je die juist wil kopiëren.env*(kan ok zijn, maar dan moet je Dockerfile het niet verwachten)
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
- Pas
.dockerignoreaan zodat noodzakelijke bestanden niet worden uitgesloten. - Gebruik expliciete
COPY-paden en kopieer alleen wat je nodig hebt.
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).
Oorzaak C: Symlinks in je project (monorepo, shared libs) die buiten de context wijzen
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:
node_modulesmet symlinks (pnpm, workspaces)packages/foo -> ../shared/foo- lokale libs gelinkt met
npm link poetry/Python met editable installs- Git submodules die niet zijn ingecheckt
Controleer symlinks
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
- Kopieer geen symlink-structuren; kopieer broncode en installeer dependencies in de container.
- Gebruik een monorepo-strategie: bouw vanuit de repo-root en zet de context op root.
- Vermijd
COPY . .als dat symlinks meeneemt die naar buiten wijzen. - Voor pnpm: gebruik een Dockerfile die pnpm correct gebruikt en geen host
node_moduleskopieert.
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
- Fix permissions op host:
chmod -R u+rwX,go+rX .
- Of sluit problematische paden uit in
.dockerignore(bijv..git,tmp,coverage, lokale caches).
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:
- Je hebt
Src/op disk, maarCOPY src ./srcin Dockerfile. - Of je importeert
./UserServicemaar het bestand heet./userservice.ts.
Controle
Check de echte namen:
ls -la
find . -maxdepth 2 -type d -print
Oplossing
- Hernoem mappen/bestanden consistent.
- Forceer git om case-only renames te pakken:
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
- Zorg dat CI submodules ophaalt.
- Of vendor de dependency.
- Of genereer artifacts in de Docker build (bijv. compileer in build stage).
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
- Vermijd
ADDvoor downloads; gebruikcurl/wgetinRUNmet checksums. - Gebruik
COPYvoor lokale bestanden.
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.
Stap 4: Zoek symlinks en rare files
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:
- Commit
package-lock.json - Verwijder regel uit
.dockerignore - Of pas Dockerfile aan:
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
- Node: niet
node_modules - Python: niet
.venv - Java: niet
.m2(tenzij je weet wat je doet) - Go: niet
bin/artifacts
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:
- herstart Docker Desktop
- eventueel “Clean / Purge data” (destructief)
8) Checklist: quickest wins
Loop deze lijst af zodra je failed to compute cache key ziet:
-
Welke stap faalt?
Bouw met:docker build --progress=plain --no-cache -t debug . -
Bestaat elk bronbestand in die
COPYecht?ls -la pad/naar/bestand -
Wordt het bestand uitgesloten door
.dockerignore?cat .dockerignore -
Zijn er symlinks die buiten de context wijzen?
find . -type l -ls -
Bouw je vanuit de juiste map / met de juiste context?
pwd -
Zijn er onleesbare bestanden/permissieproblemen?
find . -type f ! -readable -ls | head -
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
- Check file:
ls -la package-lock.json
Uitkomst: bestand bestaat niet.
- 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
-
Pin base images (zeker in productie) om verrassingen te voorkomen:
FROM node:20-alpineNog strikter:
FROM node:20-alpine@sha256:<digest> -
Gebruik multi-stage builds om build tooling niet in runtime te hebben.
-
Gebruik
RUN --mount=type=cache(BuildKit) voor snellere dependency installs (geavanceerd, maar nuttig). Voor npm:# syntax=docker/dockerfile:1.7 FROM node:20-alpine WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci COPY src ./src CMD ["node", "src/index.js"]Als je dit gebruikt, zorg dat je builder BuildKit ondersteunt (meestal wel).
-
Houd secrets uit de context: stop
.envin.dockerignoreen gebruik runtime env vars of Docker secrets.
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:
- ontbrekende bestanden in
COPY(verkeerde context, lockfile ontbreekt) .dockerignoredie te agressief is- symlinks die buiten de context wijzen (monorepo/workspaces)
- permissieproblemen of onleesbare bestanden
- CI-checkouts die submodules/artifacts missen
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.