Introduction
Docker has become the de facto standard for containerizing applications, enabling developers to build, ship, and run software quickly across various environments. However, one of the most common issues users encounter is the infamous “permission denied” error. Whether you’re running Docker commands on the host machine, building images, or executing operations inside a container, file permissions and user privileges can sometimes block you from moving forward.
In this comprehensive guide, we will explore why the permission denied error occurs in Docker, how Linux file permissions and Docker’s security model interact, and walk through multiple solutions to fix these errors. We will also cover advanced topics such as SELinux and AppArmor contexts, best practices for volume mounts, and how to manage user privileges both on the host and inside containers.
Understanding Linux File Permissions and Docker
To grasp why permission denied errors happen in Docker, it’s essential to have a basic understanding of Linux file permissions. In Linux, every file and directory has three categories of permissions: owner, group, and others. For each category, three types of permissions exist: read (r
), write (w
), and execute (x
).
When you run a Docker container, files and directories on your host are accessed by processes that may run as different UIDs (user IDs) or GIDs (group IDs). If the Docker daemon or the process inside the container tries to access a file it doesn’t have permission for, you’ll see a permission denied error. Furthermore, Docker employs additional security mechanisms like namespaces, capabilities, and mandatory access control (e.g., AppArmor or SELinux), all of which can influence file access.
Basic Permission Bits
- Read (r): Allows viewing file contents or listing directory contents.
- Write (w): Allows modifying file contents or creating/deleting files in a directory.
- Execute (x): Allows executing a file or traversing a directory.
You can check permissions with ls -l
, which outputs something like:
-rwxr-xr-- 1 alice developers 4096 Jun 1 10:00 script.sh
In this example, owner is alice
, group is developers
, and others have read-only access. If a process runs as a user outside of this scope, it won’t be able to write or execute the file.
Common Causes of Permission Denied in Docker
Before diving into specific solutions, let’s enumerate the most frequent reasons Docker users see a permission denied error:
- Docker CLI Invocation without Sudo: Running
docker
commands as a non-root user who is not in thedocker
group. - Incorrect Ownership of Files on Host: Mounting host directories or files into the container that are owned by a different user or group not accessible by the container’s process.
- File Mode Bits: Host files or directories might not have the execute bit for scripts or the required read/write bit for data files.
- SELinux or AppArmor Restrictions: Security policies preventing Docker processes from reading or writing certain paths.
- Permissions inside Container: The user inside the container does not have correct UID/GID or capabilities to access a file/directory.
- Volume Mount Mismatch: Host volumes mounted with root-only permissions, causing the container process to be denied access.
In the sections below, we’ll tackle each of these causes with targeted fixes.
Fixing “Permission Denied” for Docker Commands on Host
When you install Docker on a Linux distribution, the Docker daemon (dockerd
) runs as root
. The Docker CLI (docker
) communicates with the daemon via a UNIX socket located at /var/run/docker.sock
. By default, this socket is owned by root:docker
and has permission bits srw-rw----
. This means only root
or users in the docker
group can communicate with the Docker daemon.
Symptoms
$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.41/containers/json: dial unix /var/run/docker.sock: connect: permission denied
This indicates your current user is not in the docker
group or you are missing sudo
privileges.
Solution Options
1. Add Your User to the docker
Group
The most common recommendation is to add your user account to the docker
group. This allows you to run Docker commands without sudo
.
# Create the docker group if it doesn’t exist
sudo groupadd docker
# Add your user (e.g., alice) to the docker group
sudo usermod -aG docker alice
# After this, you must log out and log back in for the group change to take effect
Once you log back in, verify membership:
$ id -nG
alice docker sudo
You should now be able to run:
$ docker run hello-world
2. Use sudo
for Docker Commands
If you prefer not to manage group membership, you can prefix every Docker command with sudo
:
$ sudo docker ps
Note that using sudo
is straightforward but can be cumbersome over time. It’s generally cleaner to adjust group permissions.
Fixing Permission Denied When Building Docker Images
When building images using a Dockerfile
, you may COPY or ADD files from your host into the image. If Docker cannot read these files due to host file permissions, the build will fail:
Step 3/10 : COPY app/script.sh /usr/local/bin/script.sh
COPY failed: stat /home/alice/project/app/script.sh: permission denied
Solutions
1. Ensure Read Permission on Host Files
Make sure the user running docker build
has read access to all files in the build context:
$ chmod -R u+r project/
If multiple users or CI pipelines need access, adjust group permissions:
# Assume files belong to alice:developers
sudo chown -R alice:developers project/
sudo chmod -R g+r project/
2. Correct .dockerignore
Sometimes your context directory contains files with restrictive permissions that you don’t actually need in your image (e.g., .env
or .git
directories). Exclude them in .dockerignore
to avoid permission issues and reduce build context size.
# .dockerignore
.env
.git
node_modules
Fixing Permission Denied for Mounted Volumes
One of Docker’s most powerful features is volume mounting. However, mismatched user IDs between host and container can cause permission denied errors when reading or writing data in a mounted directory.
$ docker run -v /home/alice/data:/data my-image
Error: EACCES: permission denied, open '/data/config.json'
Common Scenarios
- The host directory is owned by
alice:alice
with mode700
, but the container process runs asroot
or another user. - The container image’s user has a different UID/GID than the host file owner.
- SELinux/AppArmor is enforcing policies that block access to that path.
Solutions
1. Adjust Ownership on the Host
Change ownership of the host directory to match the user inside the container. If your container runs as root
(UID 0) by default, you could:
$ sudo chown -R root:root /home/alice/data
$ sudo chmod -R 755 /home/alice/data
If your container runs as a non-root user, adjust accordingly. For example, if the container uses UID 1000:
$ sudo chown -R 1000:1000 /home/alice/data
$ sudo chmod -R 750 /home/alice/data
2. Use --user
Flag to Match UIDs
When running the container, specify the UID and GID to match the host directory’s owner:
$ docker run --user $(id -u):$(id -g) -v /home/alice/data:/data my-image
This ensures the process inside the container runs with the same privileges as alice
on the host, avoiding permission mismatches.
3. Use Named Volumes Instead of Host Mounts
If you don’t need to inspect the files on the host directly, use a named volume. Docker will manage the volume’s ownership automatically:
$ docker volume create my-data
$ docker run -v my-data:/data my-image
By default, this volume is owned by root:root
inside the container. You can then change ownership within the Dockerfile or at runtime.
Fixing Permission Denied Inside the Container
Sometimes a container’s application fails due to permission issues at runtime:
Unhandled promise rejection Error: EACCES: permission denied, mkdir '/app/logs'
Common Causes
- The Dockerfile creates files or directories owned by
root
, but the application runs as a non-root user. - Scripts lack execute permissions (
chmod +x
). - Applications attempt to write to protected system paths.
Solutions
1. Explicitly Set Ownership in Dockerfile
In your Dockerfile
, after creating directories, set the correct owner and permissions:
FROM node:14-alpine
# Create app directory
RUN mkdir -p /usr/src/app
# Change ownership to node user
RUN chown -R node:node /usr/src/app
# Switch to non-root user
USER node
# Copy application files
WORKDIR /usr/src/app
COPY --chown=node:node package*.json ./
RUN npm install
COPY --chown=node:node . .
CMD ["npm", "start"]
By using --chown=node:node
with COPY
or RUN chown
, you ensure files are accessible to the node
user.
2. Grant Execute Permissions to Scripts
If your entrypoint or startup script lacks execute permission, the container will error out:
Step 5/7 : RUN chmod +x entrypoint.sh
Or set permissions before the build:
$ chmod +x entrypoint.sh
$ docker build -t my-image .
3. Avoid Writing to Root-Owned Directories
By default, most official images run as root
. If your application tries to write to /usr
or /etc
without proper permissions, it will fail. Instead, create a dedicated writable directory (e.g., /data
or /app
) and set ownership accordingly.
SELinux and AppArmor Restrictions
Many Linux distributions (e.g., CentOS, Fedora, RHEL) enable SELinux by default, while others (e.g., Ubuntu) use AppArmor. These mandatory access control (MAC) systems can prevent Docker from mounting or accessing certain host directories, causing permission denied errors even if file permissions appear correct.
SELinux Contexts
When you mount a host directory into a Docker container, SELinux requires the correct security context (SELinux label) on that directory. The context for Docker typically needs to be z
or Z
.
Temporary Fix with :z
or :Z
Add a :z
or :Z
suffix to your volume mount:
$ docker run -v /home/alice/data:/data:z my-image
– :z
tells Docker to share the volume content between containers (allowing multiple containers to read/write).
– :Z
tells Docker to give exclusive access to the container.
Permanent Fix with chcon
You can permanently set the SELinux context on the host directory:
$ sudo chcon -Rt svirt_sandbox_file_t /home/alice/data
This command recursively sets the SELinux type to svirt_sandbox_file_t
, which Docker containers may access by default.
AppArmor Profiles
On Ubuntu or Debian, AppArmor profiles can restrict container access. If you encounter a permission denied error even when file permissions and SELinux contexts are correct, check the AppArmor profile applied to the container. By default, Docker uses the docker-default
AppArmor profile.
Disable or Adjust AppArmor Profile
To quickly test whether AppArmor is the culprit, you can run the container with an unconfined profile:
$ docker run --security-opt apparmor=unconfined -v /home/alice/data:/data my-image
If this solves the permission denied error, consider modifying your docker-default
profile to allow access to the needed paths, or create a custom profile.
Fixing “Permission Denied” When Using docker exec
Running commands inside a running container with docker exec
can also trigger permission denied errors if the target file or directory doesn’t permit access to the default user (often root
or a non-root application user).
$ docker exec my-container cat /var/log/app.log
cat: /var/log/app.log: Permission denied
Solutions
1. Specify --user
to Match Ownership
If the file inside the container is owned by a non-root user (e.g., appuser
with UID 1001), specify that user:
$ docker exec --user 1001:1001 my-container cat /var/log/app.log
2. Adjust Permissions Inside the Container
You can temporarily change permissions inside the container if you have root
access:
$ docker exec -u root my-container chmod +r /var/log/app.log
$ docker exec my-container cat /var/log/app.log
If you need to automate this on container start, add a RUN chmod
step in your Dockerfile or an entrypoint script.
Best Practices to Avoid Permission Denied Errors
The best way to fix permission denied errors is to proactively follow best practices when creating and running Docker images.
1. Build as Non-Root User When Possible
Running containers as root
can lead to security risks. Instead, create and switch to a non-root user in your Dockerfile and ensure all necessary directories have the correct owner:
FROM python:3.9-slim
# Create a non-root user
RUN useradd --create-home appuser
USER appuser
WORKDIR /home/appuser/app
COPY --chown=appuser:appuser . .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "app.py"]
2. Set Proper Ownership and Permissions Early
Right after creating directories or copying files in your Dockerfile, change ownership so later steps don’t encounter permission issues:
RUN mkdir -p /data && chown -R appuser:appuser /data
3. Use docker-compose
with user:
and volumes:
Correctly
When using docker-compose.yml
, you can set the user and group for services and specify mount options for SELinux:
version: "3.8"
services:
web:
image: my-web-app
user: "1001:1001"
volumes:
- type: bind
source: ./data
target: /data
bind:
propagation: rshared
selinux: true
The selinux: true
option appends :z
to the mount, ensuring the correct SELinux context.
4. Leverage Multi-Stage Builds to Minimize Permissions Complexity
Multi-stage builds can help keep sensitive files out of final images and reduce the need for complex permission management. Build as root
in an intermediate stage, then copy only necessary artifacts into a non-root final stage:
FROM golang:1.18 AS builder
WORKDIR /build
COPY . .
RUN go build -o myapp
FROM alpine:3.15
RUN apk add --no-cache ca-certificates
COPY --from=builder /build/myapp /usr/local/bin/myapp
RUN adduser -D appuser
USER appuser
CMD ["/usr/local/bin/myapp"]
Summary
Permission denied errors in Docker often stem from mismatches between file ownership, permission bits, and the user or security context under which Docker processes operate. By understanding Linux file permissions, Docker’s security model, and tools like SELinux and AppArmor, you can diagnose and resolve these errors effectively. Key takeaways:
- Add your user to the
docker
group or usesudo
to avoid “permission denied” for Docker CLI commands. - Ensure host files and directories have appropriate read/write/execute bits before building images or mounting as volumes.
- Match UIDs/GIDs between host and container when mounting volumes, or adjust ownership using
chown
. - Grant execute permissions for scripts used during image build or container startup.
- Account for SELinux and AppArmor policies by using
:z
/:Z
flags or adjusting security context withchcon
. - Build images with a non-root user whenever possible and set ownership early in the Dockerfile.
- Leverage multi-stage builds and
docker-compose
user settings for cleaner, more secure images.
Following these best practices and troubleshooting steps will help you maintain a smooth, secure Docker workflow free from permission issues.
Frequently Asked Questions (20 FAQs)
1. Why do I get “permission denied” when running docker ps
?
This happens because your user is not in the docker
group. Either add your user to the group with sudo usermod -aG docker $USER
and re-login, or run Docker commands using sudo
.
2. How do I fix “COPY failed: permission denied” during docker build
?
Ensure the files being copied have read permissions for the Docker build context user. Run chmod -R u+r path/to/files
or adjust file ownership with chown
so Docker can read them.
3. Why can’t my container write to a mounted volume?
The host directory might be owned by a different UID/GID than the one used inside the container. Use chown -R
on the host to match the container user, or run Docker with --user $(id -u):$(id -g)
to align UIDs.
4. What does :z
suffix do when mounting volumes?
On SELinux-enabled systems, appending :z
changes the security context of the mounted directory to allow multiple containers to share it. It ensures Docker can read/write the volume without SELinux blocking access.
5. How do I permanently fix SELinux context for a host directory?
Use sudo chcon -Rt svirt_sandbox_file_t /path/to/dir
. This recursively sets the SELinux type to svirt_sandbox_file_t
, which Docker containers can access by default.
6. My container logs say “permission denied” for a script. What should I check?
Verify the script has execute permissions (chmod +x script.sh
) and that the user inside the container owns the script. In your Dockerfile, use RUN chmod +x /path/to/script.sh
or COPY --chown=user:user
.
7. Can I run Docker commands without sudo
on Ubuntu?
Yes. Add your user to the docker
group (sudo usermod -aG docker $USER
), then log out and back in. You can then run Docker commands without sudo
.
8. Why do I get “permission denied” inside the container when reading a file I just copied?
The file may be owned by root
with restrictive permissions. Use COPY --chown=user:user
in the Dockerfile or add RUN chown user:user /path/to/file
to give the container’s user access.
9. How do I adjust AppArmor profile if it’s blocking container file access?
Test by disabling AppArmor (--security-opt apparmor=unconfined
). If it works, create or modify a custom AppArmor profile to allow access to the required paths or keep the container unconfined if security requirements allow.
10. Is it safe to run containers as root
?
Running as root
inside containers can be a security risk. It’s best to create a non-root user in your Dockerfile, set appropriate ownership, and switch to that user before running your application.
11. Why does docker exec
show “permission denied” on a file owned by root?
By default, docker exec
runs as root
. If you’re targeting a file owned by another user, specify --user UID:GID
to match ownership. Alternatively, adjust file permissions inside the container with chmod
.
12. My CI/CD pipeline fails with permission errors during docker build
. How can I fix this?
Ensure the CI user has read permissions on all files in the build context. In your CI script, run chmod -R u+r .
or adjust ownership to the CI user’s UID/GID before invoking docker build
.
13. How do I avoid permission issues when copying files between Windows host and Linux container?
On Windows, use Git Bash or WSL to ensure proper UNIX permissions. After cloning your repo, run chmod +x ./script.sh
before building the image. Alternatively, use git config core.fileMode false
to preserve execution bits.
14. Can Docker Desktop on Mac/Windows face permission denied errors?
Yes. When mounting OS X or Windows folders, Docker Desktop uses a virtualization layer. Ensure the shared folder is enabled in Docker Desktop’s preferences and that your user has read/write permission on that directory in the host OS.
15. Why does docker-compose
fail with permission denied on volume mounts?
Check that docker-compose.yml
specifies correct mount options (volumes:
with :z
or :Z
for SELinux). Ensure host directory ownership matches user:
in your service definition or adjust ownership on the host.
16. How can I troubleshoot SELinux blocking container access?
Check Audit logs (/var/log/audit/audit.log
) for AVC denials. If you see denied operations, use audit2allow
to generate a custom policy module or apply :z
on volume mounts.
17. My Docker build is slow after fixing permission issues—why?
Adding chmod
or chown
in a Dockerfile can increase build time. To optimize, combine file system operations in fewer layers, use multi-stage builds, and minimize the number of file permission changes.
18. Why can’t my non-root application write to /var/log
inside the container?
The /var/log
directory is owned by root
. Create a dedicated logs directory in your Dockerfile:
RUN mkdir -p /app/logs && chown -R appuser:appuser /app/logs
Then point your application to write logs there instead.
19. Will changing file permissions on the host affect other applications?
Yes. Changing ownership or permissions on a shared directory can impact other services relying on those files. Limit changes to project-specific folders or use group-based permissions to isolate impacts.
20. How do I prevent future permission denied issues in Docker?
Follow these best practices:
- Use non-root users in Dockerfiles.
- Set file ownership and permissions explicitly during build.
- Align UIDs/GIDs for volume mounts.
- Use named volumes for data persistence when possible.
- Account for SELinux/AppArmor early by using
:z
/:Z
or adjusting security contexts.