What this usually means
The 'permission denied' error in Docker comes in two distinct flavors. The first is a client-side error: your user doesn't have access to the Docker daemon socket (/var/run/docker.sock), usually because you're not in the docker group or you're running as a non-root user without sudo. The second is a container-level error: the binary or script inside the container doesn't have execute permissions, or the container's user namespace mapping blocks access. A third, less common cause is SELinux or AppArmor denying the container's operation. Misdiagnosing which layer is failing wastes hours.
The first ten minutes — establish facts before touching code.
- 1Run 'docker version' as the same user. If you get 'permission denied', the issue is socket access.
- 2Check user groups: 'groups $USER' — if 'docker' is missing, you are not in the docker group.
- 3For container-level errors: run 'docker run --rm -it alpine sh' and inside, 'ls -l' the script. If script lacks 'x', it's file permissions.
- 4If using custom images, check the Dockerfile: 'RUN chmod +x script' is missing.
- 5Examine SELinux: 'getenforce' — if 'Enforcing', try 'chcon -t container_file_t /path/to/volume' or use ':Z' mount flag.
- 6Check user namespaces: 'docker info | grep -i userns' — if enabled, the container's root may be mapped to non-root host user.
The specific files, logs, configs, and dashboards that usually own this bug.
- search/var/run/docker.sock — socket permissions (should be srw-rw----, owned by root:docker)
- search/etc/group — does your user belong to the 'docker' group?
- searchDockerfile — look for COPY/ADD permissions, missing chmod, or USER directive with insufficient rights
- searchContainer entrypoint/script — check execute bits and shebang (#!/bin/bash vs #!/bin/sh)
- searchSELinux context: 'ls -Z' on bind-mounted directories; 'audit2allow' to decode denials
- searchDocker daemon config: /etc/docker/daemon.json for userns-remap or selinux-enabled
- searchSystem logs: 'journalctl -u docker' or 'dmesg | grep denied' for SELinux denials
Practical causes, not theory. These are the things you will actually find.
- warningUser not in the docker group — most common on fresh Linux installs.
- warningThe binary or script inside the container lacks execute permission — often from COPY without chmod.
- warningBind-mounted host file has restrictive permissions — NFS or root-owned files inside container user namespace.
- warningSELinux enforcing with wrong context on mounted volumes — deny the container from writing/executing.
- warningUser namespace remapping — root in container maps to non-root host user, can't access host files.
- warningDocker daemon socket has wrong permissions — accidental chmod or non-default group.
- warningUsing docker-compose with privileged: false but container needs host-level access.
Concrete fix directions. Pick the one that matches your root cause.
- buildAdd user to docker group: 'sudo usermod -aG docker $USER' then log out and back in.
- buildIn Dockerfile, add 'RUN chmod +x /path/to/script' after COPY, or set permissions in the COPY command.
- buildFor bind mounts, use ':Z' flag (SELinux) or set ownership with '--user' flag to match host UID.
- buildDisable user namespace remapping: in /etc/docker/daemon.json, set 'userns-remap': '' and restart docker.
- buildIf SELinux is enforcing, set context: 'chcon -Rt container_file_t /host/dir' or relabel with ':z'.
- buildRun the container with '--privileged' only as last resort — it disables all security features.
A fix you cannot prove is a guess. Close the loop.
- verifiedRun 'docker run hello-world' — if it works, socket permission issue is resolved.
- verifiedFor internal permission: 'docker run --rm myimage /bin/sh -c "ls -l /app/script"' shows permissions.
- verifiedTest SELinux: 'docker run --rm -v /host/dir:/container/dir:Z myimage' and see if error disappears.
- verifiedCheck user namespace: 'docker run --rm --userns=host alpine id' should show uid 0.
- verifiedRun 'docker info | grep -i security' to confirm SELinux/AppArmor status.
Things that make this bug worse or harder to find.
- warningRunning 'sudo docker' as a workaround — it bypasses the socket permission but adds security risk and breaks volume mounts.
- warningChanging /var/run/docker.sock permissions to 777 — security hole.
- warningAdding a user to docker group and not logging out/in — the group change isn't effective until new session.
- warningUsing 'chmod 777' on host-mounted files — insecure; use proper UID mapping.
- warningBlindly adding '--privileged' — disables all container security; use only if you understand the implications.
- warningEditing daemon.json without restarting docker daemon — changes don't take effect until restart.
The Midnight Deploy That Denied
Timeline
- 23:15On-call alert: CI job fails with 'docker: permission denied while trying to connect to the Docker daemon socket'.
- 23:18SSH into Jenkins node; run 'docker ps' as jenkins user — same error.
- 23:20Check 'groups jenkins' — missing docker group. 'sudo usermod -aG docker jenkins'.
- 23:22Re-run Jenkins job — still fails. Realize group change needs new session.
- 23:25Restart Jenkins agent service. Job passes docker pull but fails on run: 'OCI runtime create failed: permission denied'.
- 23:30Inspect Dockerfile: COPY script.py ./script.py without chmod. Add 'RUN chmod +x script.py'.
- 23:35Rebuild image, push, re-run job — success.
It was a Friday night, and I was the on-call engineer for our CI platform. A critical Python service deploy was failing with a 'permission denied' error in Docker. The team had recently migrated Jenkins to a new Ubuntu 20.04 node, and nobody had added the jenkins user to the docker group. I quickly fixed that, but the real puzzle was the subsequent error after a rebuild.
The second error was 'OCI runtime create failed: exec user process caused: permission denied'. This was inside the container — the Python script copied from the build context lacked execute permission. The Dockerfile had a simple COPY . /app, but no chmod. The entrypoint was set to /app/script.py, but the file wasn't executable.
I added a RUN chmod +x /app/script.py to the Dockerfile, rebuilt the image, and pushed it. The next Jenkins run succeeded. The root cause was two-fold: the socket permission was a red herring that masked the real permission issue inside the container. The lesson: always verify the entire chain, and never assume a single fix works.
Root cause
Missing execute permission on the Python script inside the container (COPY without chmod), compounded by the jenkins user not being in the docker group.
The fix
Add jenkins to docker group (requires new session) and add RUN chmod +x /app/script.py to Dockerfile.
The lesson
When debugging Docker permission errors, distinguish between client-side socket access and container-level execution permissions. A group fix may take effect only after a new session. Always rebuild images with explicit execute permissions.
The first thing to determine is whether the error occurs before or after the container starts. If you see 'permission denied while trying to connect to the Docker daemon socket', your user cannot talk to the Docker daemon. This is a client-side error. If you see 'OCI runtime create failed' or 'exec user process caused: permission denied', the container image is built but the runtime cannot execute the entrypoint or command. These are completely different failure domains.
To test the client side, run 'docker version' as your user. If it fails, the issue is socket permissions. For the container side, run a simple image like 'docker run --rm alpine echo hello'. If that works but your custom image fails, the problem is inside the image. Always isolate the layer.
The Docker socket /var/run/docker.sock is owned by root:docker with permissions 660. Users in the docker group can read/write the socket. Adding a user to the docker group is the standard fix, but many forget to start a new login session. The group change is not effective in the current shell. You must log out and back in, or use 'newgrp docker' or 'sg docker -c "your command"'.
Never run 'sudo chmod 666 /var/run/docker.sock' or 'sudo chown youruser /var/run/docker.sock'. That exposes the socket to all users and breaks security. Instead, add the user to the docker group. Also, check if SELinux is blocking socket access: 'sudo audit2allow -a' may show denials.
When you COPY a script into a Docker image, the file permissions are preserved from the build context. If the source file is not executable (no 'x' bit), the container cannot run it as an entrypoint or command. The fix is to set the execute bit in the Dockerfile: 'RUN chmod +x /path/to/script'. Alternatively, you can use COPY --chmod=+x script /path/ in newer Docker versions.
Another subtle issue is the shebang line. If the script starts with '#!/usr/bin/python' but python is not installed in the container, you get a 'permission denied' because the kernel cannot find the interpreter. Always use '#!/usr/bin/env python' or ensure the interpreter path is correct.
When you mount a host directory into a container, the files retain their original UID/GID from the host. If the container runs as a non-root user (e.g., USER app with UID 1000) and the mounted file is owned by root (UID 0), the container user gets 'permission denied'. Fix by either running the container with '--user 0' (root) or adjusting the host file permissions to match the container user.
User namespace remapping (userns-remap) maps the container's root to a non-root host user. This is a security feature but can cause 'permission denied' on bind mounts because the container's root can't access host files owned by the real root. To test, run 'docker info | grep -i userns'. If enabled, you can either disable it or ensure host files are world-readable.
On systems with SELinux enforcing (Red Hat, Fedora, CentOS), the container runtime is confined by policy. If you mount a volume with the default context, SELinux may deny the container from reading or writing files. The error may appear as 'permission denied' without mentioning SELinux. Check with 'getenforce' and look in /var/log/audit/audit.log for 'avc: denied' messages.
Fix by adding the ':Z' or ':z' flag to the volume mount: '-v /host/dir:/container/dir:Z'. The 'Z' flag relabels the host file to the container's private context. 'z' shares the context among multiple containers. Alternatively, change the SELinux boolean: 'sudo setsebool -P container_manage_cgroup 1' if the error involves cgroups.
Frequently asked questions
I added my user to the docker group but still get 'permission denied'. What's wrong?
The group change does not apply to your current shell session. You need to log out and log back in, or run a new shell with the group: 'newgrp docker'. If that doesn't work, check if SELinux is blocking socket access with 'sudo audit2allow -a'.
My container runs fine locally but fails on the CI server with 'permission denied'. Why?
The CI server likely runs as a different user (e.g., jenkins) that is not in the docker group. Also, the CI build context may have different file permissions. Ensure the Dockerfile sets execute bits explicitly with 'RUN chmod +x'.
What does 'standard_init_linux.go:228: exec user process caused: permission denied' mean?
This error means the container runtime (runc) could not execute the entrypoint or command because the file lacks execute permission or the interpreter is missing. Check the file's permissions inside the container and the shebang line.
Can I use 'sudo docker' as a workaround?
You can, but it's not recommended for production because it bypasses user permissions and can cause file ownership issues with bind mounts. It's better to fix the underlying permission problem.
How do I fix SELinux 'permission denied' for Docker volumes?
Run the container with volume mount flag ':Z' (e.g., '-v /host/dir:/container/dir:Z') to relabel the host directory. Alternatively, change the SELinux context with 'chcon -Rt container_file_t /host/dir'.