KhueApps
Home/DevOps/Fix Docker bind mount error: source path does not exist

Fix Docker bind mount error: source path does not exist

Last updated: October 07, 2025

What this error means

Error: bind mount failed: source path does not exist

Docker could not find the host-side path you asked it to mount into the container. The path must exist and be reachable by the Docker daemon (which might be running on a VM, WSL, or a remote host).

Common causes:

  • Typo or wrong relative path
  • Using a path that exists on your machine but not on the Docker host/context
  • Windows path conversion issues (PowerShell vs Git Bash)
  • Missing directory or insufficient permissions
  • SELinux/AppArmor labeling blocks access
  • macOS/Windows: path not shared with Docker Desktop

Quickstart: make it work now

  • Always create the directory before running the container.
  • Use an absolute path when in doubt.
  • Ensure you’re mounting a path on the Docker host you’re actually using (check docker context).

Minimal working example (Linux/macOS)

mkdir -p "$PWD/data"
echo "hello" > "$PWD/data/hello.txt"

docker run --rm \
  -v "$PWD/data:/data:ro" \
  alpine:3.20 sh -c 'ls -la /data && cat /data/hello.txt'

Minimal working example (Windows PowerShell)

New-Item -ItemType Directory -Force "$PWD/data" | Out-Null
Set-Content -Path "$PWD/data/hello.txt" -Value "hello"

docker run --rm `
  -v "${PWD}/data:/data:ro" `
  alpine:3.20 sh -c "ls -la /data && cat /data/hello.txt"

If you use Git Bash on Windows and see path conversion issues, prefix the command with:

MSYS_NO_PATHCONV=1 docker run -v "/c/Users/you/project/data:/data" alpine:3.20 ls /data

Minimal Compose example

version: "3.9"
services:
  app:
    image: alpine:3.20
    command: ["sh", "-c", "ls -la /data && cat /data/hello.txt"]
    volumes:
      - type: bind
        source: ./data
        target: /data
        read_only: true

Run:

mkdir -p data && echo "hello" > data/hello.txt
docker compose up --abort-on-container-exit --remove-orphans

Step-by-step diagnosis and fixes

  1. Confirm the Docker host/context
  • Check where Docker commands execute:
    • docker context ls
  • If you’re using a remote context or Docker Desktop with WSL, the source path must exist on that host, not just on your local filesystem.
  1. Verify the path exists and is correct
  • Use absolute paths for quick checks: on Linux/macOS run pwd; on Windows PowerShell use ${PWD}.
  • Test the path:
    • Linux/macOS: ls -ld "$PWD/data"
    • Windows PowerShell: Get-Item "$PWD/data"
  1. Create the source directory/file
  • mkdir -p "$PWD/data"
  • Ensure the parent directories exist.
  1. Check permissions
  • Linux/macOS: ensure your user (and Docker daemon) can read the directory: ls -ld data
  • On SELinux distros (RHEL/Fedora/CentOS), add :z or :Z to relabel for the container:
docker run --rm -v "$PWD/data:/data:Z" alpine:3.20 ls /data
  1. Fix platform-specific path issues
  • Windows PowerShell: prefer "${PWD}/subdir:/container". Avoid bare C:\ paths unless quoted.
  • Git Bash: disable path mangling with MSYS_NO_PATHCONV=1 or use /c/Users/... form.
  • macOS/Windows (Docker Desktop): ensure the drive/path is shared. Keep the path inside your user directory if possible.
  • WSL: ensure the path is accessible to the Docker daemon (e.g., use /mnt/c/... or a path inside the WSL distro if the daemon runs there).
  1. Resolve relative vs Compose working dir
  • Compose resolves relative paths from the directory of the compose.yaml file.
  • If using variables, confirm expansion: volumes: - "${PWD}/data:/data" or "${HOME}/data:/data".
  1. Avoid symlink surprises
  • Mount the real path rather than a symlink that points outside shared areas. On macOS/Windows, symlinks to unshared locations will fail.
  1. Re-run with a simple container
  • Alpine or BusyBox with ls/cat is perfect for validating the mount before launching your real service.

Platform path cheatsheet

PlatformExample host pathExample flag
Linux/macOS/home/user/project/data-v /home/user/project/data:/data
Windows PowerShell${PWD}/data-v "${PWD}/data:/data"
Windows Git Bash/c/Users/user/project/dataMSYS_NO_PATHCONV=1 -v "/c/Users/user/project/data:/data"
WSL/mnt/c/Users/user/project/data-v "/mnt/c/Users/user/project/data:/data"

Compose patterns that work

  • Explicit bind with variable expansion:
services:
  web:
    image: nginx:1.27
    volumes:
      - type: bind
        source: ${PWD}/site
        target: /usr/share/nginx/html
  • Home directory mount (portable):
services:
  app:
    image: node:22-alpine
    working_dir: /app
    volumes:
      - ${HOME}/projects/demo:/app

Note: Ensure ${HOME}/projects/demo exists before starting Compose.

Common pitfalls

  • Using ~ in paths without confirming expansion; prefer ${HOME} or absolute paths.
  • Trailing slashes rarely matter but can hide typos; check carefully.
  • Mounting files vs directories: if you intend a single file, the exact file must exist.
  • Docker Desktop not authorized to access the drive or network share.
  • Using a path that only exists on the client while pointing DOCKER_HOST to a remote daemon.

Performance notes

  • Bind mounts on macOS/Windows are slower due to VM/FS translation. Options:
    • Mount only what you need (avoid mounting the whole repo when only src/ is required).
    • Use :cached or :delegated on Docker Desktop for read-heavy/write-heavy cases:
docker run --rm -v "$PWD/site:/usr/share/nginx/html:ro,cached" nginx:1.27
  • For heavy I/O, consider copying sources into the image at build time or use named volumes for runtime data.
  • On Linux, bind mounts are fast; prefer them for development but still avoid mounting large, constantly changing trees if not needed.

Tiny FAQ

Q: Why does it work with docker build but fail with docker run? A: Builds use the build context sent to the daemon; run-time bind mounts require the path to exist on the host visible to the daemon.

Q: Can Docker create the host path for me? A: Treat the source path as must-exist. Create it explicitly with mkdir -p to avoid surprises and permission issues.

Q: How do I check which daemon I’m using? A: Run docker context ls. If it’s not default, ensure the source path exists on that host.

Q: Do I need :z or :Z on all systems? A: Only on SELinux-enforcing systems when labeling prevents container access. Use :Z for private labels, :z for shared.

Series: Docker

DevOps