RUN and Caching

RUN, Layer Caching and Combining Commands

RUN executes a command in a temporary container and saves the changes as a layer. BuildKit 10,294 caches each step, keyed by the instruction and, for COPY, by a checksum of the files' content; when a key changes, that step and all later ones rerun. Because npm 2,036 ci depends only on the two manifests, copied before the source, a code change reruns only the final COPY:

Changing app.js reuses the npm ci layerJavaScript
touch app.js
docker build --progress=plain -t l3-booknest-api:1.0 . 2>&1 | grep -A1 -E '^#[0-9]+ \[5/5\]'
echo "// edited at $(date +%s)" >> app.js
docker build --progress=plain -t l3-booknest-api:1.0 . 2>&1 | grep -A1 -E '^#[0-9]+ \[[2-5]/'
git checkout app.js && docker build -q -t l3-booknest-api:1.0 . >/dev/null
Output
#11 [5/5] COPY . .
#11 CACHED
#8 [2/5] WORKDIR /app
#8 CACHED
...
#10 [4/5] RUN npm ci --omit=dev && npm cache clean --force
#10 CACHED
--
#11 [5/5] COPY . .
#11 DONE 0.1s
Updated 1 path from the index

touch changed only a timestamp, so even the last COPY came from the cache; the real edit invalidated only COPY . ., and the last line restores the file and rebuilds. Combine commands that belong together with && in one RUN: files deleted in a later layer still ship (Layers), so npm cache clean runs in the same step as npm ci, just as apt-get update && apt-get install -y --no-install-recommends ... && rm -rf /var/lib/apt/lists/* belong together.