Fixing 'EACCES: permission denied' in Docker: A Guide to Node.js Permissions

intermediate🐳 Docker2026-07-27| Docker, Node.js (Alpine/Debian), Linux, macOS, or Windows WSL2

Error Message

Error: EACCES: permission denied, mkdir '/app/node_modules/.cache'
#docker#nodejs#devops#security

The ContextRunning containers as root is a major security risk. To fix this, most official Node.js images include a built-in non-root user named node. However, switching to this user often leads to a frustrating wall: permission errors. You'll likely see this when npm install runs, or when frameworks like Next.js or Vite try to write to a .cache folder during startup.

This error usually pops up right after you add USER node to your Dockerfile. The build or runtime process crashes with a specific message:

Error: EACCES: permission denied, mkdir '/app/node_modules/.cache'

The Root CauseThe problem is simple ownership. When Docker executes commands like WORKDIR or COPY, it defaults to the root user (UID 0). If these commands create your /app folder, then root owns it. When you later switch to the node user (typically UID 1000), that user doesn't have permission to modify files created by root.

Common triggers include:

  • Default COPY: Running COPY . . without flags makes root the owner of every file in the container.- Volume Overwrites: Mounting a local folder over node_modules can sync host permissions that don't match the container's internal user.- Automatic Directory Creation: The WORKDIR instruction creates missing paths as root by default.## Step-by-Step Fix### 1. Use the --chown Flag in Your DockerfileThe most reliable fix is to ensure the node user owns the directory before you even copy your code. You must also tell Docker to change ownership during the copy process using the --chown flag.
# Use a specific version for stability
FROM node:18-alpine

# Pre-create the app directory with correct permissions
RUN mkdir -p /home/node/app && chown -R node:node /home/node/app

WORKDIR /home/node/app

# Switch to the non-root user early
USER node

# Copy package files first to leverage Docker cache
COPY --chown=node:node package*.json ./

RUN npm install

# Copy the rest of the source code
COPY --chown=node:node . .

CMD ["npm", "start"]

2. Handling Volumes in Docker ComposeLocal development often breaks permissions because your host machine (Mac/Windows/Linux) has different User IDs than the container. To prevent your host's node_modules from clobbering the container's version, use an anonymous volume. This keeps the container-side folder managed by Docker's internal engine.

services:
  web:
    build: .
    user: "node"
    volumes:
      - .:/home/node/app
      - /home/node/app/node_modules # This anonymous volume protects internal permissions
    environment:
      - NODE_ENV=development

3. The Quick Fix for Running ContainersIf you need to fix a container that is already running, you can force an ownership change using docker exec as the root user. This is a temporary band-aid, but it works in a pinch:

docker exec -u root -it <container_id> chown -R node:node /home/node/app/node_modules

Verifying the FixCheck if your changes worked by inspecting the directory ownership inside the container. Run this command:

docker exec -it <container_id> ls -la /home/node/app

In the output, look for the node_modules row. It should look like this:

drwxr-xr-x 100 node node 4096 Oct 20 10:00 node_modules

If the columns still show root root, your chown command was either missed or a volume mount is overriding it.

Prevention and TipsLinux permissions can be a headache, especially when UIDs don't align between your laptop and your container. Visualizing the permission bits helps avoid mistakes.

I often use the Unix Permissions Calculator on ToolCraft to verify my chmod logic. It’s a simple browser tool that converts checkboxes (Read/Write/Execute) into the exact numeric code you need. It runs locally in your browser, so it's safe for checking sensitive configurations. Key Takeaways:

  • Always use --chown: Never use COPY without --chown=node:node if you are using a non-root user.- Order matters: Create your WORKDIR and set permissions before you switch the USER.- Protect node_modules: Use anonymous volumes in Compose to prevent host-side permission conflicts.- Avoid 777: Never use chmod 777 to fix errors. It is a massive security hole. Stick to chown to grant access only to the user who needs it.

Related Error Notes