I have a very simple node js app and the project structure looks like this.
index.js
package.json
package-lock.json
Dockerfile
FROM node:12.18.2-alpine
WORKDIR /test-app
COPY package.json package-lock.json ./
RUN npm i
COPY . ./
EXPOSE 3000
ENTRYPOINT [ "node", "index.js" ]
docker-compose.yml
version: '3.2'
services:
test-app:
build: .
ports:
- "3000:3000"
volumes:
- .:/test-app
- "test_app_node_modules:/test-app/node_modules"
volumes:
test_app_node_modules:
driver: local
If you look at the volumes
section in docker-compose.yml
file, first I'm bind mounting my current directory on the host machine to the test-app
directory on the container. This means :
node_modules
that were installed in the test-app
dir of the container, during docker build, were overwritten as well.and the next step in the volumes
section is named volumes. This means:
test-app/node_modules
inside container to test_app_node_modules
volume. But the test-app/node_modules
is empty because step 1 overwrote it.If this is so, it should be causing missing dependency error but my app is running properly. I'm not sure where I'm getting node_modules from.
Also, I see an empty node_modules folder in the host directory. I assume the reason behind this is "test_app_node_modules:/test-app/node_modules"
looks for the node_modules
in the container but it doesn't exist so it creates one and as a result, it gets reflected back to the host dir.
I'm not able to grasp the idea of volume mounting. What is the flow here? How node_modules are begin stored into the volumes when there are none?
At a purely mechanical level, a couple of things happen here.
Docker sorts the volume mounts. It knows that /test-app/node_modules
is a subdirectory of /test-app
. So Docker will
node_modules
directory if required to be a mount pointThis is where the empty node_modules
directory on the host comes from: Docker creates it to be a mount point, after it's done the bind mount, so the changes there are reflected in the host content.
When the named volume is mounted in the container, if and only if the named volume is empty, Docker will copy the content from the image into the volume; this specific step ignores that there's already a volume mounted in the container. (This also works if the volume is an anonymous volume, which you see in other Node examples; it does not work for host bind mounts, if older content is in the volume, or on Kubernetes.)
So that's why this apparently works. As the other answers to this question note, this isn't an especially effective use of Docker. I'd also recommend just deleting these volumes:
, and directly running node index.js
on the host if you need a live development setup.