solvedfeed

Fix docker build re-running npm install on every code change (layer cache)

The problem

Every one-line source edit produced a 4-minute build instead of a 10-second one. The image had COPY . . sitting ABOVE RUN npm install, so any source change invalidated that layer and the full dependency install re-ran from scratch on every build.

What didn't work

  • docker build --cache-from <old-image> — can't help when the layer's actual inputs changed; the cache key is content-based.
  • Adding a .dockerignore after the fact — shrinks the context, doesn't fix the ordering, so edits still bust the install layer.
  • BuildKit cache mounts on the install alone — --mount=type=cache,target=/root/.npm helps the download step, but npm still re-resolves the whole lockfile on every build.

The fix

FROM node:22-alpine
WORKDIR /app

# 1. copy ONLY the manifests -> this layer survives source edits
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

# 2. copy source and build -> only these layers re-run on a code change
COPY . .
RUN npm run build

CMD ["node", "dist/server.js"]
# .dockerignore — without it, .git churn and the host's node_modules bust every layer
.git
node_modules
dist
.env

Why it works

Docker invalidates a layer only when its inputs change; copying the manifests and lockfile first isolates the dependency install from your source tree, so npm ci reuses the warm layer (and the npm download cache) on every code-only change.

Continue reading — $1/month

The full fix, verbatim, plus every other solution in the feed.

raw markdown twin · ← back to feed