Using docker cache with NPM dependencies

By Todd Wilson June 24, 2019

There’s been a frequent pattern that I’ve come across in maintaining local project dependencies across a team. Changing npm contexts can be taxing and when the complexity of maintaining node packages and configuration continues to get in the way of development workflow.

The amount of time it takes to install node modules adds up over time and can be reduced using docker caching. I came across an article that talks about the time saved using multi-stage docker builds and uses an external mounted volume for the node_modules path.

Here is the v2 docker-compose.builder.yml file that will run the npm install and build routines:

version: '2'
services:
  base:
    image: node:11
    volumes:
      - .:/usr/src/service
      - nodemodules:/usr/src/service/node_modules      
    working_dir: /usr/src/service

  install:
    extends:
      service: base
    command: npm i

  build:
    extends:
      service: base
    command: npm run build

volumes:
  nodemodules:
    external: true

To make it easier to run these tasks, here is the Makefile so we can run make install:

install:
	docker-compose -f docker-compose.builder.yml run --rm install

build:
	docker-compose -f docker-compose.builder.yml run --rm build

dev:
	docker-compose up

setup:
	docker volume create nodemodules

You’ll notice the external volume, you’ll need to create that first by running make setup before you can run make install. This will map the local node_modules path to the container and with multi-step caching, will speed up subsequent installs.

Here’s the dockerfile I’m using for my VueJS application, this uses NGINX to serve the distributed files:

# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

The default.conf file needed to be included with the NGINX configuration (be sure to change the server_name to match your domain):

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /usr/share/nginx/html;

  index index.html;

  server_name you.server.com;

  location / {
    try_files $uri $uri/ @rewrites;
  }

  location @rewrites {
    rewrite ^(.+)$ /index.html last;
  }

  location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    # Some basic cache-control for static files to be sent to the browser
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
  }

}

You can run the docker build with the dockerfile and default.conf in place which will allow you to run the container, here’s an example:

docker build -t todoubled/vuejs-client:latest .
docker run -it -p 8080:80 --rm --name dockerize-vuejs-app-1 todoubled/vuejs-client:latest

Lastly, here is the docker-compose v3 example for running the vueJS application in development:

version: '3'
services:
  dev:
    image: node:11
    volumes:
      - nodemodules:/usr/src/service/node_modules
      - .:/usr/src/service
    working_dir: /usr/src/service
    command: npm run serve
    ports:
      - 8080:8080

volumes:
  nodemodules:
    external: true

To summarize, you can now run a few make commands to leverage the docker-compose file(s) and docker cache setup and in a more reliable, performant way.

  1. make setup : will create the nodemodules volume
  2. make install : will install the npm dependencies and cache them in the docker image for future checks.
  3. make dev : will run the development environment task for local
  4. make build : will run the build and export files to the /dist folder

You now have a set of tools to help with local npm tasks and modules and a docker image for running the distributed source to check against for performance. My hope is that this will save a collective team time in the long run and provide a consistent environment to build on.

Custom web development services. Managed Hosting & WordPress Platform.