docker
node_modules
dockerignore
software-development
troubleshooting

Confusion with node_modules being ignored by .dockerignore

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The main source of confusion is that .dockerignore only affects the build context sent to docker build. It does not delete files already created inside the image, and it does not control what bind mounts later place into the running container.

What .dockerignore Actually Does

When you run docker build ., Docker sends a build context to the daemon. That context is basically the set of files from the chosen directory that Docker is allowed to see for COPY and ADD instructions.

.dockerignore filters that context before the build begins.

So if your .dockerignore contains:

text
node_modules

then the host machine's node_modules directory is not sent to the build context.

That means this Dockerfile will not copy host node_modules into the image:

dockerfile
1FROM node:20
2WORKDIR /app
3COPY . .
4CMD ["npm", "start"]

But that does not mean the image or container will never contain node_modules.

Why node_modules Still Appears

There are two very common reasons node_modules still exists.

The first is that the Dockerfile installs dependencies inside the image.

dockerfile
1FROM node:20
2WORKDIR /app
3COPY package.json package-lock.json ./
4RUN npm ci
5COPY . .
6CMD ["npm", "start"]

Even if .dockerignore excludes the host node_modules, the RUN npm ci step creates /app/node_modules inside the image filesystem. That is normal and usually desirable.

The second reason is bind mounts in development.

bash
docker run --rm -it -v "$PWD:/app" my-node-image

That mount overlays /app with your host directory at runtime. If your host has node_modules, the container sees them. If your host does not have node_modules, the bind mount can hide the image's /app/node_modules entirely. This is the part that surprises people most.

Build-Time Versus Run-Time Is the Real Distinction

A good mental model is:

  • '.dockerignore affects build-time file transfer'
  • 'RUN npm ci affects the image filesystem during build'
  • volume mounts affect what the container sees at run time

These are three separate stages. Problems happen when they are treated as one mechanism.

A Good Node.js Docker Pattern

For production images, a standard pattern is:

  • ignore host node_modules
  • copy only manifest files first
  • run npm ci in the image
  • copy application source afterward

Example:

dockerfile
1FROM node:20-alpine
2WORKDIR /app
3COPY package.json package-lock.json ./
4RUN npm ci --omit=dev
5COPY . .
6CMD ["node", "server.js"]

And the matching .dockerignore:

text
1node_modules
2npm-debug.log
3.git
4.env

This keeps the build context small and avoids copying host-installed binaries that may be incompatible with the target container OS.

Why Context Location Matters

.dockerignore is read from the build context root, not from wherever you wish it had been. If you run:

bash
docker build -f docker/Dockerfile .

the build context is . and Docker uses .dockerignore from that root directory.

If you instead run:

bash
docker build docker/

then the context is docker/, and only a .dockerignore in that directory matters.

Many "Docker ignored my ignore file" bugs are actually context-path mistakes.

Development Containers Need Extra Care

In local development, many teams mount the project folder into the container to get live reload. That is fine, but it changes the node_modules behavior.

A common workaround is to mount the source code while keeping node_modules in a named volume or in a different path.

yaml
1services:
2  app:
3    build: .
4    volumes:
5      - .:/app
6      - app_node_modules:/app/node_modules
7volumes:
8  app_node_modules:

This prevents the host bind mount from clobbering the container's installed dependencies.

Common Pitfalls

The biggest mistake is expecting .dockerignore to remove files created by RUN npm install or RUN npm ci. It only filters files from the host build context.

Another mistake is forgetting that a bind mount can hide files already present in the image.

A third issue is building from the wrong context directory, which makes the wrong .dockerignore file apply or no ignore file apply at all.

Finally, copying host node_modules into Linux containers from macOS or Windows is usually the wrong idea because native dependencies may be built for the wrong platform.

Summary

  • '.dockerignore only controls what files from the host are sent to docker build.'
  • It does not prevent npm ci inside the image from creating node_modules.
  • Bind mounts can override what the running container sees, including hiding image-installed dependencies.
  • The build context directory determines which .dockerignore file is used.
  • For Node.js images, ignore host node_modules and install dependencies inside the image.
  • In development, use volumes carefully so source-code mounts do not break dependency layout.

Course illustration
Course illustration

All Rights Reserved.