Containers Intermediate

Resolving ‘Invalid Directory’ Errors for Docker Compose Relative Volume Mounts on CentOS Stream / Rocky Linux

Troubleshoot and fix common Docker Compose relative path volume mount issues on CentOS Stream or Rocky Linux, often caused by SELinux or pathing errors.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Troubleshoot and fix common Docker Compose relative path volume mount issues on CentOS Stream or Rocky Linux, often caused by SELinux or pathing errors.

Introduction

As a seasoned Systems Administrator, you've likely encountered the frustration of a container failing to start or an application within it unable to access its required data. When working with Docker Compose on RHEL-based systems like CentOS Stream or Rocky Linux, using relative path volume mounts can sometimes lead to cryptic "invalid directory" or "permission denied" errors. This guide delves into the common causes of these issues, primarily focusing on SELinux enforcement, and provides a precise, step-by-step resolution to get your services running smoothly.

The symptom often manifests as a container that repeatedly crashes, fails to initialize, or reports file system errors upon startup. While the docker-compose.yml syntax may appear correct, the underlying host system's security mechanisms, especially SELinux, frequently interfere with the Docker daemon's ability to bind-mount directories into containers.

Symptom & Error Signature

When attempting to start your Docker Compose services using docker compose up -d, you might see errors similar to these in your terminal or in the output of docker logs <container_name>:

Error response from daemon: error while mounting volume '/var/lib/docker/volumes/my_app_data/_data': mkdir /var/lib/docker/volumes/my_app_data/_data: permission denied

Or, more commonly when bind-mounting host directories:

Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/path/to/your/host/directory" to rootfs "/var/lib/docker/overlay2/<container_id>/merged" at "/app/data" caused: stat /path/to/your/host/directory: permission denied: unknown

In some cases, if the relative path resolves to a non-existent directory or one with incorrect permissions preventing Docker from creating it, you might also see:

ERROR: for my_service  Cannot start service my_service: failed to create task for container: failed to create shim task: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/path/to/your/host/directory" to rootfs "/var/lib/docker/overlay2/<container_id>/merged" at "/app/data" caused: stat /path/to/your/host/directory: no such file or directory: unknown

Root Cause Analysis

The "invalid directory" or "permission denied" errors for Docker Compose relative path volume mounts on CentOS Stream / Rocky Linux primarily stem from three core issues:

  1. SELinux Enforcement (Primary Suspect): Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) security mechanism that restricts processes from accessing resources (like files and directories) unless explicitly permitted by a defined policy. On CentOS Stream and Rocky Linux, SELinux is enabled and enforced by default. The Docker daemon runs within a specific SELinux context, and it is inherently restricted from accessing arbitrary host file system locations for bind mounts unless those locations have the correct SELinux labels (e.g., container_file_t) or the mount operation explicitly requests a re-labeling. Relative paths in docker-compose.yml are resolved to absolute paths by Docker, and it's these absolute paths that SELinux evaluates.

  2. Standard Linux File Permissions: While SELinux often takes precedence, traditional Discretionary Access Control (DAC) permissions (user, group, others, rwx) can also prevent the Docker daemon or the user running docker compose from accessing or creating the host directory. If the user running docker compose (or the docker daemon's effective user) lacks read/write/execute permissions on the host path, the mount will fail.

  3. Incorrect Relative Paths or Non-existent Directories: A common oversight is a misunderstanding of how relative paths are interpreted. In docker-compose.yml, a relative path for a volume mount (e.g., ./data) is always relative to the location of the docker-compose.yml file itself, not necessarily the current working directory where you execute docker compose up. If the directory specified does not exist on the host system at the resolved absolute path, and permissions/SELinux prevent Docker from creating it, the mount will fail with a "no such file or directory" error.

Step-by-Step Resolution

Follow these steps meticulously to diagnose and resolve your Docker Compose volume mount issues.

1. Verify Absolute Paths and Directory Existence

First, ensure that the intended host directory for your volume mount exists and that your docker-compose.yml is referencing it correctly.

Navigate to the directory containing your docker-compose.yml file:

cd /path/to/your/docker-compose-project

Inspect your docker-compose.yml for the volume mount definition. For example:

# docker-compose.yml
version: '3.8'
services:
  my-app:
    image: my-image:latest
    volumes:
      - ./data:/app/data # Relative path example
      # - /absolute/path/on/host/data:/app/data # Absolute path example

Using the relative path specified (e.g., ./data), check its existence and current permissions from your project directory:

ls -ld ./data

If the directory does not exist, create it:

mkdir -p ./data

Always ensure the directory structure on the host machine matches what Docker Compose expects. Docker Compose typically creates the host directory if it doesn't exist for bind mounts, but only if it has sufficient permissions and SELinux doesn't interfere.

2. Inspect Standard Linux File Permissions

Verify that the user running the Docker daemon (typically root) and potentially the user running docker compose (if using rootless Docker or specific configurations) has the necessary permissions to access the host directory.

sudo ls -la /path/to/your/host/directory # Replace with the actual absolute path

Example output:

drwxr-xr-x. 2 root root 4096 Aug 29 10:00 .

If permissions are too restrictive, you might need to adjust them. For testing, you could temporarily loosen them, but for production, use specific user/group ownership or ACLs.

# Example: Grant read/write access to everyone (for testing, not recommended for production)
sudo chmod -R 777 /path/to/your/host/directory

# Example: Change ownership to a specific user (e.g., the user running docker compose if in docker group)
# Assuming 'dockeruser' is your user and is in the 'docker' group
sudo chown -R dockeruser:dockeruser /path/to/your/host/directory

Setting chmod -R 777 is generally a security risk and should only be used for debugging. Always strive for the principle of least privilege in production environments.

3. Address SELinux (Most Common Solution)

SELinux is the most frequent cause of "permission denied" errors for Docker volume mounts on CentOS/Rocky Linux. You have a few options to resolve this, listed from most recommended to least.

Option A: Recommended – Using z or Z Mount Options in docker-compose.yml

Docker provides a built-in mechanism to handle SELinux contexts for bind mounts directly within your docker-compose.yml file. This is the cleanest and most portable solution.

  • z: Tells Docker to label the bind mount content with container_file_t. This allows all containers to read/write to the content. Use this if multiple containers or services need to access the same volume.
  • Z: Tells Docker to label the bind mount content with a private unshared label (svirt_s<ID>_l<ID>). This makes the content accessible only to the specific container creating the mount. Use this for sensitive data or when only one container needs access.

Modify your docker-compose.yml to append :z or :Z to your volume mount:

# docker-compose.yml
version: '3.8'
services:
  my-app:
    image: my-image:latest
    volumes:
      - ./data:/app/data:z # Use :z for shared access
      # - ./more-data:/app/more-data:Z # Use :Z for private access (single container)

After modifying the docker-compose.yml, bring down and then up your services to apply the changes:

docker compose down
docker compose up -d
Option B: Persistent SELinux Context Labeling (For Pre-existing Directories)

If you have an existing directory on your host that you want to consistently use for Docker containers without modifying docker-compose.yml (e.g., for system-level data directories), you can permanently label it with the correct SELinux context.

First, identify the absolute path to your host directory. Then, use semanage fcontext to add a new file context rule and restorecon to apply it.

# Replace /path/to/your/host/directory with the actual path
sudo semanage fcontext -a -t container_file_t "/path/to/your/host/directory(/.*)?"
sudo restorecon -Rv "/path/to/your/host/directory"
  • semanage fcontext -a -t container_file_t: Adds a rule to the SELinux file context configuration, specifying that the target directory should be labeled with container_file_t. The (/.*)? ensures that all subdirectories and files within it also inherit this label.
  • restorecon -Rv: Recursively applies the updated SELinux contexts based on the new rules to the specified path.

Verify the SELinux context:

ls -ldZ /path/to/your/host/directory

You should see container_file_t in the output, e.g.:

drwxr-xr-x. 2 root root unconfined_u:object_r:container_file_t:s0 4096 Aug 29 10:00 /path/to/your/host/directory

Now, try starting your Docker Compose services:

docker compose up -d
Option C: Temporarily Disable SELinux (Diagnostic Only)

This step is purely for diagnostic purposes to confirm if SELinux is indeed the root cause. Never disable SELinux in a production environment.

To temporarily switch SELinux to permissive mode (it logs but does not enforce policies):

sudo setenforce 0

Now, attempt to start your Docker Compose services:

docker compose up -d

If your services start successfully, you've confirmed that SELinux was the issue. Re-enable SELinux immediately:

sudo setenforce 1

Running with SELinux disabled compromises the security of your system. Always re-enable it and use one of the persistent solutions (Option A or B) for production environments.

4. Restart Docker Daemon (If Issues Persist)

In rare cases, especially after making significant system-level changes or if the Docker daemon state is inconsistent, restarting the daemon can help.

sudo systemctl restart docker
sudo systemctl status docker

5. Review Docker Daemon Logs

If the problem persists after trying the above solutions, consult the Docker daemon's logs for more detailed error messages. This can provide clues if the issue is more complex or related to other Docker configuration problems.

sudo journalctl -u docker -f

This command will show real-time logs from the Docker daemon. Look for any ERRO or WARN messages related to volume mounts or container startup.

By systematically applying these troubleshooting steps, particularly focusing on SELinux configuration, you should be able to resolve "invalid directory" and "permission denied" errors when using Docker Compose volume mounts on CentOS Stream and Rocky Linux.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.