I've been learning about Docker and I had a Dockerfile like this one:
FROM node
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm cache clean --force && rm -rf node_modules
CMD ['npm', 'start']
Then I learned about multi-stage build and changed the Dockerfile to this:
FROM node AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:lts-alpine
WORKDIR /app
COPY --from=builder /dist /app
CMD ['npm', 'start']
I imagined I could remove the npm cache clean --force && rm -rf node_modules
since these artifacts wouldn't be present in the final image and the build would be a little bit faster. But then I read somewhere that those artifacts would still use some space, being present in the cache.
So I did a test:
- Run
docker builder prune --all
; - I built with
npm cache clean --force && rm -rf node_modules
- Run
docker builder prune --all
again. Total: 912.7MB - I built without
npm cache clean --force && rm -rf node_modules
. - Run
docker builder prune --all
again. Total: 912.7MB (just like before).
Running docker history <image>
right after each build didn't show any difference either. So are the npm cache and node_modules really cached by docker and using some space somewhere and I'm missing something or can I get away with the faster Dockerfile not minding cleaning them up? Just keeping the cleaning would be a good practice?
I've been learning about Docker and I had a Dockerfile like this one:
FROM node
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm cache clean --force && rm -rf node_modules
CMD ['npm', 'start']
Then I learned about multi-stage build and changed the Dockerfile to this:
FROM node AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:lts-alpine
WORKDIR /app
COPY --from=builder /dist /app
CMD ['npm', 'start']
I imagined I could remove the npm cache clean --force && rm -rf node_modules
since these artifacts wouldn't be present in the final image and the build would be a little bit faster. But then I read somewhere that those artifacts would still use some space, being present in the cache.
So I did a test:
- Run
docker builder prune --all
; - I built with
npm cache clean --force && rm -rf node_modules
- Run
docker builder prune --all
again. Total: 912.7MB - I built without
npm cache clean --force && rm -rf node_modules
. - Run
docker builder prune --all
again. Total: 912.7MB (just like before).
Running docker history <image>
right after each build didn't show any difference either. So are the npm cache and node_modules really cached by docker and using some space somewhere and I'm missing something or can I get away with the faster Dockerfile not minding cleaning them up? Just keeping the cleaning would be a good practice?
1 Answer
Reset to default 0The particular Dockerfile setup you show makes no difference in terms of space. A multi-stage Dockerfile helps when
- You need some set of libraries only to build the application, but not to run it; and
- In the final build stage you only copy or install the specific things you need into the image.
Since you're copying the entire /app
directory in, the final image is basically the same as the build stage. You're right that RUN rm ...
does not decrease the image size; but conversely, you can't discard the node_modules
directory either, because that contains the libraries your application needs to run.
Let's say, hypothetically, that you have a Typescript-based application. Your package.json
file lists tsc
in the devDependencies
section: you need to compile Typescript to plain Javascript, but having done that, you do not actually need the Typescript compiler any more. Here you could usefully have two stages
- Install
tsc
and all of thedevDependencies
, and compile the application; then - Install only the
dependencies
but not thedevDependencies
, plus the compiled application but not the original.ts
files.
That Dockerfile might look like
FROM node:lts AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY ./ ./
RUN npm run build
FROM node:lts
WORKDIR /app
COPY package.json package-lock.json ./
ENV NODE_ENV=production
RUN npm ci
COPY --from=build /app/dist/ /app/dist/
CMD ["node", "dist/index.js"]
Since the second stage sets NODE_ENV=production
, the npm ci
step omits the devDependencies
, and you'll get a smaller final image.
docker history
shows the layers in the final image. Not the cache. And as you've seen, cleaning the cache doesn't affect the final image. It's probably correct that the cache is bigger if you don't clean it up, but it's not something I'd worry about. – Hans Kilian Commented 19 hours agodocker history
! But what aboutdocker builder prune -a
freeing the exact same space? Does that mean the cache didn't change or maybe I'm mistaken about something else? – Noberto Pessoa Commented 19 hours ago