DeployU
Interviews / Cloud & DevOps / A container can't write to its mounted volume. Fix the permission issue.

A container can't write to its mounted volume. Fix the permission issue.

debugging Volumes & Storage Interactive Quiz Code Examples

The Scenario

You’re running a Node.js application that needs to write logs to a mounted volume:

$ docker run -d \
    --name api \
    --user 1001:1001 \
    -v /data/logs:/app/logs \
    api:latest

$ docker logs api
Error: EACCES: permission denied, open '/app/logs/app.log'

The container runs as a non-root user (security best practice), but now it can’t write to the mounted volume.

The Challenge

Fix the permission issue while maintaining security best practices. Explain the relationship between container user IDs and host filesystem permissions.

Wrong Approach

A junior engineer might remove the --user flag to run as root, chmod 777 the host directory, run chmod inside the container hoping it helps, or just use a different path. Running as root defeats security hardening, chmod 777 is a security vulnerability, chmod inside the container doesn't affect mounted volumes, and changing paths doesn't solve the underlying issue.

Right Approach

A senior engineer understands that the container's UID must match or have permission to access the host directory. Solutions include: setting correct ownership on the host, using named volumes instead of bind mounts, or running an init container to fix permissions. The key insight is that UIDs, not usernames, determine access rights across the container-host boundary.

Understanding the Problem

# Check who owns the host directory
ls -la /data/logs
# drwxr-xr-x 2 root root 4096 Jan 15 10:00 /data/logs

# Container runs as UID 1001, but directory is owned by root
# UID 1001 has no write permission

Key Insight: Docker doesn’t map usernames between host and container. It uses UIDs directly. A user named nodejs with UID 1001 in the container is treated as UID 1001 on the host, regardless of what that UID is called on the host.

Solution 1: Fix Host Directory Ownership

# Create directory with correct ownership
sudo mkdir -p /data/logs
sudo chown 1001:1001 /data/logs

# Verify
ls -la /data/
# drwxr-xr-x 2 1001 1001 4096 Jan 15 10:00 logs

# Now the container can write
docker run -d \
    --name api \
    --user 1001:1001 \
    -v /data/logs:/app/logs \
    api:latest
# Create a named volume
docker volume create app-logs

# Run container with named volume
docker run -d \
    --name api \
    --user 1001:1001 \
    -v app-logs:/app/logs \
    api:latest

Why named volumes are better:

  • Docker manages permissions automatically
  • Portable across hosts
  • Easier backup and migration
  • Better for production workloads

Solution 3: Init Container Pattern

In Docker Compose, use an init container to fix permissions:

version: '3.8'

services:
  # Init container fixes permissions
  init-permissions:
    image: busybox
    command: chown -R 1001:1001 /data
    volumes:
      - app-data:/data
    user: root

  # Application container
  api:
    image: api:latest
    user: "1001:1001"
    volumes:
      - app-data:/app/logs
    depends_on:
      init-permissions:
        condition: service_completed_successfully

volumes:
  app-data:

Solution 4: Match UIDs in Dockerfile

FROM node:18-alpine

# Use a UID that matches your host system
ARG UID=1001
ARG GID=1001

RUN addgroup -g ${GID} -S nodejs && \
    adduser -S nodejs -u ${UID} -G nodejs

WORKDIR /app
COPY --chown=nodejs:nodejs . .

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

Build with matching UID:

# Build with UID that matches host directory owner
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t api:latest .

Permission Debugging Commands

# Check container's user
docker exec api id
# uid=1001(nodejs) gid=1001(nodejs)

# Check mounted directory permissions inside container
docker exec api ls -la /app/logs

# Check host directory permissions
ls -la /data/logs

# Test write access
docker exec api touch /app/logs/test.txt

# Check volume mount details
docker inspect api --format='{{json .Mounts}}' | jq

Common Permission Scenarios

ScenarioHost OwnerContainer UserResultFix
Defaultroot (0)root (0)Works-
Secure containerroot (0)1001Failschown 1001 on host
Named volumeDocker1001Works-
SELinux enabledroot (0)1001FailsAdd :z or :Z suffix

SELinux/AppArmor Considerations

On systems with SELinux (RHEL/CentOS) or AppArmor:

# Add :z for shared volume (multiple containers)
docker run -v /data/logs:/app/logs:z api:latest

# Add :Z for private volume (single container)
docker run -v /data/logs:/app/logs:Z api:latest

Warning: :Z relabels the host directory. Don’t use it on system directories!

Practice Question

A container running as UID 1001 can't write to a bind-mounted directory owned by root. What's the most secure fix?