When Docker-Compose throws a "No such file or directory" error despite the file physically existing, this typically indicates a permission or path resolution issue at the system level rather than a simple file-not-found scenario. Let's examine the key symptoms from the case:
ERROR: compose.cli.main.main: .IOError: [Errno 2] No such file or directory: '/etc/docker/docker-compose.d/20-registry.yaml'
Yet the file verification shows:
root@srv-backup:/# ll /etc/docker/docker-compose.d/20-registry.yaml
-rwxrwxr-x+ 1 root root 842 Jan 24 15:19 /etc/docker/docker-compose.d/20-registry.yaml*
In Linux environments, several factors could cause this behavior:
- ACLs or Extended Attributes: The
+
in permissions (-rwxrwxr-x+
) indicates existing ACLs - Namespace Isolation: Docker daemon might be running in a different mount namespace
- SELinux/AppArmor: Security contexts preventing access
- Symbolic Link Resolution: The path might contain symlinks that resolve differently for the daemon
First, verify the actual path visibility to the Docker daemon:
docker exec -it $(docker ps -q --filter ancestor=registry) ls -la /etc/docker/docker-compose.d/
Check for mount namespace differences:
ls -la /proc/$(pgrep dockerd)/root/etc/docker/docker-compose.d/
Solution 1: Verify ACL restrictions
getfacl /etc/docker/docker-compose.d/20-registry.yaml
Solution 2: Temporary bypass for testing
cp /etc/docker/docker-compose.d/20-registry.yaml /tmp/
chmod 644 /tmp/20-registry.yaml
docker-compose -f /tmp/20-registry.yaml up
Solution 3: Check for mount propagation
If using Docker with special storage drivers or snap packages:
findmnt -o TARGET,PROPAGATION /etc/docker
For persistent cases, strace can reveal the actual syscall failure:
strace -f -e trace=file docker-compose -f /etc/docker/docker-compose.d/20-registry.yaml up 2>&1 | grep '20-registry.yaml'
Check kernel audit logs for SELinux/AppArmor denials:
ausearch -m avc -ts recent | grep docker
dmesg | grep -i denied
For production systems, consider these architectural improvements:
# Recommended directory structure
/etc/docker/
├── compose.d/
│ ├── 20-registry.yaml
│ └── ...
├── config/
└── data/
# Example systemd unit override
[Service]
ReadWritePaths=/etc/docker/compose.d
ReadWritePaths=/etc/docker/config
Remember that Docker snap installations often have stricter confinement rules than native packages.
Recently while performing a bare metal recovery of my Docker server, I encountered a particularly stubborn issue with docker-compose. Despite verifying the physical existence of my compose file, the tool kept insisting it couldn't be found:
docker-compose --verbose -f /etc/docker/docker-compose.d/20-registry.yaml up
compose.config.config.find: Using configuration files: /etc/docker/docker-compose.d/20-registry.yaml
ERROR: compose.cli.main.main: .IOError: [Errno 2] No such file or directory: '/etc/docker/docker-compose.d/20-registry.yaml'
Yet, the file was clearly present with proper permissions:
ls -l /etc/docker/docker-compose.d/20-registry.yaml
-rwxrwxr-x+ 1 root root 842 Jan 24 15:19 /etc/docker/docker-compose.d/20-registry.yaml
After thorough investigation, I discovered several potential causes for this behavior:
- Filesystem Namespace Isolation: Docker daemon might be running in a different mount namespace
- SELinux/AppArmor Restrictions: Security policies blocking access
- Symbolic Link Resolution: Broken links in the path chain
- Mount Point Visibility: The /etc/docker directory might not be visible to the container runtime
Here are some diagnostic commands that helped me identify the root cause:
# Check mount namespace
lsns -t mnt
# Verify SELinux context
ls -Z /etc/docker/docker-compose.d/20-registry.yaml
# Check real path resolution
realpath /etc/docker/docker-compose.d/20-registry.yaml
# Test file access as docker user
sudo -u docker cat /etc/docker/docker-compose.d/20-registry.yaml
In my case, the solution involved two steps:
# 1. Temporarily disable SELinux enforcement
setenforce 0
# 2. Copy the file to a different location and try
cp /etc/docker/docker-compose.d/20-registry.yaml /tmp/
docker-compose -f /tmp/20-registry.yaml up
If this works, you've confirmed it's a security policy or mount namespace issue. For a permanent fix:
# For SELinux:
chcon -R -t container_file_t /etc/docker
# For AppArmor:
# Add the following to your docker-default profile
/etc/docker/** r,
If the above doesn't work, consider these alternatives:
# 1. Bind mount the directory when running docker-compose
docker-compose -f /host/path/to/file.yaml up --project-directory=/host/path
# 2. Use docker-compose from within a container
docker run --rm -v /etc/docker:/etc/docker docker/compose:1.29.2 \
-f /etc/docker/docker-compose.d/20-registry.yaml up
To avoid similar issues in the future:
- Store compose files in /opt/docker instead of /etc/docker
- Use relative paths for volumes and other file references
- Document all required SELinux/AppArmor policies
- Test recovery procedures regularly