How to cache the RUN npm install instruction when docker build a Dockerfile How to cache the RUN npm install instruction when docker build a Dockerfile docker docker

How to cache the RUN npm install instruction when docker build a Dockerfile


Ok so I found this great article about efficiency when writing a docker file.

This is an example of a bad docker file adding the application code before running the RUN npm install instruction:

FROM ubuntuRUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.listRUN apt-get updateRUN apt-get -y install python-software-properties git build-essentialRUN add-apt-repository -y ppa:chris-lea/node.jsRUN apt-get updateRUN apt-get -y install nodejsWORKDIR /opt/appCOPY . /opt/appRUN npm installEXPOSE 3001CMD ["node", "server.js"]

By dividing the copy of the application into 2 COPY instructions (one for the package.json file and the other for the rest of the files) and running the npm install instruction before adding the actual code, any code change wont trigger the RUN npm install instruction, only changes of the package.json will trigger it. Better practice docker file:

FROM ubuntuMAINTAINER David Weinstein <david@bitjudo.com># install our dependencies and nodejsRUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.listRUN apt-get updateRUN apt-get -y install python-software-properties git build-essentialRUN add-apt-repository -y ppa:chris-lea/node.jsRUN apt-get updateRUN apt-get -y install nodejs# use changes to package.json to force Docker not to use the cache# when we change our application's nodejs dependencies:COPY package.json /tmp/package.jsonRUN cd /tmp && npm installRUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/# From here we load our application's code in, therefore the previous docker# "layer" thats been cached will be used if possibleWORKDIR /opt/appCOPY . /opt/appEXPOSE 3000CMD ["node", "server.js"]

This is where the package.json file added, install its dependencies and copy them into the container WORKDIR, where the app lives:

ADD package.json /tmp/package.jsonRUN cd /tmp && npm installRUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/

To avoid the npm install phase on every docker build just copy those lines and change the ^/opt/app^ to the location your app lives inside the container.


Weird! No one mentions multi-stage build.

# ---- Base Node ----FROM alpine:3.5 AS base# install nodeRUN apk add --no-cache nodejs-current tini# set working directoryWORKDIR /root/chat# Set tini as entrypointENTRYPOINT ["/sbin/tini", "--"]# copy project fileCOPY package.json .## ---- Dependencies ----FROM base AS dependencies# install node packagesRUN npm set progress=false && npm config set depth 0RUN npm install --only=production # copy production node_modules asideRUN cp -R node_modules prod_node_modules# install ALL node_modules, including 'devDependencies'RUN npm install## ---- Test ----# run linters, setup and testsFROM dependencies AS testCOPY . .RUN  npm run lint && npm run setup && npm run test## ---- Release ----FROM base AS release# copy production node_modulesCOPY --from=dependencies /root/chat/prod_node_modules ./node_modules# copy app sourcesCOPY . .# expose port and define CMDEXPOSE 5000CMD npm run start

Awesome tuto here: https://codefresh.io/docker-tutorial/node_docker_multistage/


I've found that the simplest approach is to leverage Docker's copy semantics:

The COPY instruction copies new files or directories from and adds them to the filesystem of the container at the path .

This means that if you first explicitly copy the package.json file and then run the npm install step that it can be cached and then you can copy the rest of the source directory. If the package.json file has changed, then that will be new and it will re-run the npm install caching that for future builds.

A snippet from the end of a Dockerfile would look like:

# install node modulesWORKDIR  /usr/appCOPY     package.json /usr/app/package.jsonRUN      npm install# install applicationCOPY     . /usr/app