Search code examples
node.jsdockerdockerfile

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


I am currently developing a Node backend for my application. When dockerizing it (docker build .) the longest phase is the RUN npm install. The RUN npm install instruction runs on every small server code change, which impedes productivity through increased build time.

I found that running npm install where the application code lives and adding the node_modules to the container with the ADD instruction solves this issue, but it is far from best practice. It kind of breaks the whole idea of dockerizing it and it cause the container to weight much more.

Any other solutions?


Solution

  • 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 ubuntu
    
    RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
    RUN apt-get update
    RUN apt-get -y install python-software-properties git build-essential
    RUN add-apt-repository -y ppa:chris-lea/node.js
    RUN apt-get update
    RUN apt-get -y install nodejs
    
    WORKDIR /opt/app
    
    COPY . /opt/app
    RUN npm install
    EXPOSE 3001
    
    CMD ["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 ubuntu
    MAINTAINER David Weinstein <david@bitjudo.com>
    
    # install our dependencies and nodejs
    RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
    RUN apt-get update
    RUN apt-get -y install python-software-properties git build-essential
    RUN add-apt-repository -y ppa:chris-lea/node.js
    RUN apt-get update
    RUN 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.json
    RUN cd /tmp && npm install
    RUN 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 possible
    WORKDIR /opt/app
    COPY . /opt/app
    
    EXPOSE 3000
    
    CMD ["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.json
    RUN cd /tmp && npm install
    RUN 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.