The 'cannot open: Permission denied' error occurs when a process inside a Docker container cannot read or write to system files like /etc/passwd. This typically happens due to user namespace remapping, read-only filesystem mounts, SELinux policies, or running commands that require root privileges as a non-root user.
The "cannot open '/etc/passwd': Permission denied" error indicates that a process inside your Docker container is trying to access the /etc/passwd file (or similar system files) but lacks the necessary permissions. This error commonly occurs in these scenarios: - Commands like `useradd`, `usermod`, `groupadd`, or `passwd` need to modify /etc/passwd but the container runs as a non-root user - You've mounted /etc/passwd from the host into the container, and the mount prevents modifications - SELinux or AppArmor security policies are blocking access to the file - Rootless Docker mode uses user namespace remapping that changes effective permissions - The container's filesystem is mounted read-only or with restricted capabilities The error is particularly common in CI/CD pipelines, containers that dynamically create users at runtime, and setups using rootless Docker or Podman.
If you need to run user management commands, ensure you're running as root:
# Check current user
docker exec <container_name> whoami
# Run command as root
docker exec -u root <container_name> useradd newuser
# Or enter an interactive shell as root
docker exec -u 0 -it <container_name> /bin/shIn your Dockerfile, perform user operations before switching to a non-root user:
FROM ubuntu:22.04
# Run user operations as root (default)
RUN useradd -m appuser
# Then switch to non-root user
USER appuser
CMD ["./app"]If you've mounted /etc/passwd or /etc/shadow from the host, user modification commands will fail because mount points cannot be replaced:
# This causes problems:
docker run -v /etc/passwd:/etc/passwd:ro myimage
# The passwd command creates a temp file and tries to rename it,
# which fails on a mount pointSolutions:
1. Don't mount individual files - mount a directory instead if needed:
docker run -v /etc:/etc myimage2. Create users in the Dockerfile instead of at runtime:
RUN useradd -u 1000 -m appuser3. Use NSS (Name Service Switch) for user mapping instead of mounting passwd files:
# Use libnss-extrausers or similar for dynamic user databasesRootless Docker and Podman use user namespaces that remap UIDs/GIDs, which can cause permission issues:
# Check if you're running rootless Docker
docker info | grep -i rootless
# Check user namespace mappings
cat /etc/subuid
cat /etc/subgidSolutions for rootless mode:
1. Use --userns=host to disable user namespace for a container:
docker run --userns=host myimage
# Note: This reduces security isolation2. Ensure proper subuid/subgid configuration:
# Check your user has enough subordinate UIDs
grep $USER /etc/subuid
# Should show: username:100000:65536 (or similar)
# If missing, add them:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER3. For Podman, use the :U volume option:
podman run -v ./data:/app/data:U myimage
# :U tells Podman to chown the volume to match the container userSELinux may block container access to certain files. Check if SELinux is the cause:
# Check SELinux status
getenforce
# Check for SELinux denials
sudo ausearch -m avc -ts recent
# Or check the audit log
sudo grep denied /var/log/audit/audit.log | tailSolutions:
1. Add the :z or :Z flag to volume mounts:
# :z allows the volume to be shared between containers
docker run -v ./data:/app/data:z myimage
# :Z makes the volume private to this container
docker run -v ./data:/app/data:Z myimage2. Use the container_file_t context:
sudo chcon -Rt svirt_sandbox_file_t /path/to/volume3. Create an SELinux policy exception (less recommended):
# Generate a policy module from denials
sudo ausearch -m avc -ts recent | audit2allow -M mycontainer
sudo semodule -i mycontainer.ppIf your container uses a read-only root filesystem, /etc/passwd cannot be modified:
# Check if filesystem is read-only
docker inspect <container> | grep -A5 ReadonlyRootfsSolutions:
1. Mount /etc as a tmpfs for runtime modifications:
docker run --read-only --tmpfs /etc myimage2. Or mount specific files as writable volumes:
docker run --read-only \
-v passwd-data:/etc/passwd \
-v shadow-data:/etc/shadow \
myimage3. Create users in the Dockerfile before setting read-only:
FROM alpine
# Create all users while filesystem is writable
RUN adduser -D appuser
# Application can run read-only since users exist
USER appuser
CMD ["./app"]When using bind mounts, ensure the container user has matching permissions:
# Find your host user's UID/GID
id -u # e.g., 1000
id -g # e.g., 1000Build the container with matching UID:
FROM node:20
# Create user with specific UID matching host user
ARG UID=1000
ARG GID=1000
RUN groupadd -g $GID appgroup \
&& useradd -u $UID -g appgroup -m appuser
USER appuserBuild and run with matching IDs:
# Build with your UID/GID
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t myapp .
# Or pass at runtime
docker run --user $(id -u):$(id -g) myappAfter applying fixes, verify that file access works:
# Test reading /etc/passwd
docker exec <container> cat /etc/passwd
# Test user lookup
docker exec <container> id
docker exec <container> whoami
# Test user modification (if needed)
docker exec -u root <container> useradd testuser
docker exec <container> grep testuser /etc/passwd
# For volume mounts, test file creation
docker exec <container> touch /app/data/testfileIf using CI/CD, ensure your pipeline logs show successful execution without permission errors.
Understanding /etc/passwd in Containers
The /etc/passwd file maps usernames to numeric UIDs. When you run a container, Linux kernel permission checks use numeric UIDs, not usernames. The container has its own /etc/passwd that's independent from the host's file.
Commands like useradd work by:
1. Creating a temporary file
2. Writing the new passwd data
3. Renaming the temp file to /etc/passwd
This rename fails if /etc/passwd is a mount point (individual file mount) rather than a regular file.
Rootless Docker vs Rootful Docker
In rootful Docker, UID 0 inside the container is UID 0 on the host (real root). In rootless Docker, UID 0 inside the container maps to your unprivileged host UID (e.g., 1000). This is more secure but causes permission mismatches.
Check your mode:
# Rootless shows your user's home path
docker info | grep "Docker Root Dir"
# Rootless: /home/user/.local/share/docker
# Rootful: /var/lib/dockerUser Namespace Mapping
With user namespaces, container UIDs are mapped to different host UIDs:
- Container UID 0 -> Host UID 100000 (example)
- Container UID 1000 -> Host UID 101000
This mapping is defined in /etc/subuid and /etc/subgid.
Security Considerations
Avoid these insecure workarounds:
- Running containers with --privileged just to fix permission issues
- Disabling SELinux (setenforce 0) instead of fixing policies
- Using chmod 777 on host directories
Instead, properly configure user mappings and security contexts.
Container Runtimes Comparison
| Runtime | User Namespace | SELinux Support | Notes |
|---------|---------------|-----------------|-------|
| Docker (rootful) | Optional | Yes | Traditional mode |
| Docker (rootless) | Always | Limited | More secure, more complex |
| Podman | Default | Yes | :U and :Z options |
| Containerd | Configurable | Yes | Lower-level runtime |
CI/CD Specific Issues
CI/CD systems often use restricted container runtimes. If you encounter this error in CI:
1. Check if the CI system uses rootless containers
2. Avoid runtime user creation; create users in Dockerfile
3. Use docker-in-docker (dind) carefully with proper volume mounts
Error response from daemon: client version X.XX is too new. Maximum supported API version is X.XX
How to fix 'client version is too new' in Docker
dial tcp: i/o timeout
How to fix 'dial tcp: i/o timeout' in Docker
image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker