How to fix EACCES: permission denied, mkdir '/app/data' in Docker

DockerINTERMEDIATEMEDIUMUpdated Dec 6, 2025ยท 375 views

The EACCES mkdir permission denied error occurs when a process inside a Docker container cannot create a directory due to insufficient filesystem permissions. This typically happens when running as a non-root user without proper ownership of the target directory or its parent path.

Quick fix

Fixes EACCES

# Check the running user
docker exec <container_name> whoami

# Check user ID and group ID
docker exec <container_name> id

# Check ownership of the parent directory
docker exec <container_name> ls -la /app/

# Check ownership recursively
docker exec <container_name> ls -laR /app/
  1. Identify the running user and directory ownership
  2. Create directories as root before switching users
  3. Use COPY --chown for proper file ownership
  4. Handle volume mounts with matching user IDs
docker โ€” error
# Check the running user
docker exec <container_name> whoami
# Check user ID and group ID
docker exec <container_name> id
# Check ownership of the parent directory
docker exec <container_name> ls -la /app/
# Check ownership recursively
docker exec <container_name> ls -laR /app/
EACCES: permission denied, mkdir '/app/data'

What this error means

The EACCES (Error: Access) permission denied error when attempting to `mkdir` inside a Docker container indicates that the process lacks write permissions to the parent directory where you're trying to create a new folder. In Docker containers, this error commonly appears when: - Your application runs as a non-root user (via the USER directive) but the parent directory is owned by root - You mount a volume from the host where the ownership doesn't match the container user's UID/GID - The Dockerfile creates directories in the wrong order relative to the USER directive - WORKDIR is declared after USER, but the directory doesn't exist yet The error is most frequently encountered in Node.js applications trying to create directories for data storage, caches, or uploaded files, but it affects any containerized application that needs to create directories at runtime.

Typical symptoms

  • Application crashes on startup when trying to create data directories
  • mkdir command fails with 'EACCES: permission denied' inside container
  • Application works as root but fails when running as non-root user
  • Volume-mounted directories cannot be written to from inside the container
  • Build succeeds but container fails at runtime when creating directories
  • CI/CD pipeline fails during container initialization

Common causes

  1. Parent directory owned by root while process runs as non-root user
  2. USER directive placed before directory creation in Dockerfile
  3. Volume mount from host has different UID/GID than container user
  4. WORKDIR creates directory with root ownership when placed after USER
  5. Application tries to create directories in system paths (e.g., /var, /etc)
  6. Base image runs as non-root user by default but directory structure expects root

How to fix "EACCES: permission denied, mkdir '/app/data'"

1Identify the running user and directory ownership

First, determine which user your process runs as and who owns the target directory:

bash
# Check the running user
docker exec <container_name> whoami

# Check user ID and group ID
docker exec <container_name> id

# Check ownership of the parent directory
docker exec <container_name> ls -la /app/

# Check ownership recursively
docker exec <container_name> ls -laR /app/

If the user is node (UID 1000) but the directory is owned by root:root, that's the cause of your permission error.

2Create directories as root before switching users

The most reliable approach is to create all directories while still running as root, then chown them to the runtime user:

dockerfile
FROM node:20-alpine

WORKDIR /app

# Copy and install dependencies as root
COPY package*.json ./
RUN npm ci

# Copy application code
COPY . .

# Create the data directory while still root
RUN mkdir -p /app/data /app/uploads /app/cache

# Create non-root user and set ownership of everything
RUN addgroup -g 1001 -S appgroup \
    && adduser -S appuser -u 1001 -G appgroup \
    && chown -R appuser:appgroup /app

# Now switch to non-root user
USER appuser

CMD ["node", "server.js"]

This ensures all directories exist and are writable before the user switch.

3Use COPY --chown for proper file ownership

When copying files into the container, use the --chown flag to set ownership immediately:

dockerfile
FROM node:20-alpine

# Create user first
RUN addgroup -g 1001 -S appgroup \
    && adduser -S appuser -u 1001 -G appgroup

WORKDIR /app

# Create directories with correct ownership
RUN mkdir -p /app/data && chown -R appuser:appgroup /app

USER appuser

# Copy files with correct ownership
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci

COPY --chown=appuser:appgroup . .

CMD ["node", "server.js"]

The --chown flag ensures files are owned by the runtime user from the start.

4Handle volume mounts with matching user IDs

When using bind mounts, ensure the host directory permissions match the container user:

yaml
# docker-compose.yml
services:
  app:
    build: .
    volumes:
      - ./data:/app/data
    user: "${UID:-1000}:${GID:-1000}"

Before running, create the host directory with correct permissions:

bash
# Find your user ID and group ID
id -u  # UID (e.g., 1000)
id -g  # GID (e.g., 1000)

# Create the directory on the host
mkdir -p ./data

# The directory will inherit your user's ownership
ls -la ./data

Set environment variables to pass your IDs:

bash
export UID=$(id -u)
export GID=$(id -g)
docker-compose up
5Use named volumes for persistent data

Named volumes automatically handle permissions better than bind mounts:

yaml
# docker-compose.yml
services:
  app:
    build: .
    volumes:
      - app-data:/app/data

volumes:
  app-data:

Docker manages named volume permissions, and they persist across container restarts. The container user can write to them without host-side permission setup.

If you need to initialize the volume with specific ownership, use an entrypoint script:

bash
#!/bin/sh
# entrypoint.sh

# Ensure data directory is writable (runs as root initially)
chown -R appuser:appgroup /app/data 2>/dev/null || true

# Switch to non-root user and run the main command
exec su-exec appuser "$@"
6Create directories at runtime with an entrypoint script

For dynamic directory creation, use an entrypoint script that runs as root then drops privileges:

dockerfile
FROM node:20-alpine

RUN apk add --no-cache su-exec

WORKDIR /app

COPY package*.json ./
RUN npm ci
COPY . .

RUN addgroup -g 1001 -S appgroup \
    && adduser -S appuser -u 1001 -G appgroup \
    && chown -R appuser:appgroup /app

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
bash
#!/bin/sh
# entrypoint.sh

# Create runtime directories with correct permissions
mkdir -p /app/data /app/uploads /app/cache
chown -R appuser:appgroup /app/data /app/uploads /app/cache

# Drop to non-root user and execute CMD
exec su-exec appuser "$@"

This allows runtime directory creation while maintaining security.

7Verify the fix works correctly

After applying the fix, verify the permissions are correct:

bash
# Rebuild the image
docker build -t myapp .

# Run and check permissions
docker run --rm myapp ls -la /app/

# Test directory creation
docker run --rm myapp sh -c "mkdir -p /app/data/test && ls -la /app/data/"

# Check it works with your actual application
docker run --rm myapp node -e "require('fs').mkdirSync('/app/data/test', {recursive: true}); console.log('Success!')"

The directory creation should now succeed without permission errors.

Advanced notes

Understanding Linux Permissions in Containers

Docker containers share the host's kernel, meaning filesystem permissions are enforced by the same Linux permission model. The container doesn't have its own user database - it just maps numeric UIDs/GIDs to names from /etc/passwd inside the container.

A file owned by UID 1000 in the container is owned by UID 1000 on the host, regardless of what username maps to that UID in either context.

The Principle of Least Privilege

Running containers as root is convenient but insecure. A compromised application running as root inside a container can potentially escape to the host or access sensitive resources. Always:

1. Create a dedicated non-root user for your application
2. Only grant write permissions to directories that truly need it
3. Keep system directories (/usr, /etc, /bin) read-only

Rootless Docker Considerations

In rootless Docker mode, UIDs are remapped. Container UID 1000 might map to host UID 101000 (base UID + container UID). Check your mapping with:

bash
cat /etc/subuid
cat /etc/subgid

Multi-stage Builds and Permissions

When using multi-stage builds, remember that COPY --from uses the source stage's ownership. You may need to chown after copying:

dockerfile
FROM builder AS build
# ... build steps ...

FROM node:20-alpine
RUN adduser -S appuser
COPY --from=build /app/dist /app/dist
RUN chown -R appuser /app
USER appuser

Security Warning

Never use chmod 777 as a fix. While it eliminates permission errors, it allows any user (including potential attackers) to read, write, and execute files. Always use proper ownership (chown) to the specific user that needs access.

Frequently asked questions

What does "EACCES" mean?
The EACCES (Error: Access) permission denied error when attempting to mkdir inside a Docker container indicates that the process lacks write permissions to the parent directory where you're trying to create a new folder. js applications trying to create directories for data storage, caches, or uploaded files, but it affects any containerized application that needs to create directories at runtime.
How do I fix "EACCES"?
The EACCES mkdir permission denied error occurs when a process inside a Docker container cannot create a directory due to insufficient filesystem permissions. This typically happens when running as a non-root user without proper ownership of the target directory or its parent path.
What causes "EACCES"?
Parent directory owned by root while process runs as non-root user USER directive placed before directory creation in Dockerfile Volume mount from host has different UID/GID than container user
Is the "EACCES" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading