Docker Compose Volume Mapping Showing Empty Folder Inside Container
A volume mount showing empty inside the container, despite files genuinely existing at the host path, almost always comes down to a path mismatch (relative path resolved from an unexpected location) or a named volume masking the actual bind-mounted content underneath it, rather than any fundamental Docker volume bug.
The Problem
You configure a volume mapping expecting your host files to appear inside the container, but checking inside the running container shows the directory as empty:
docker compose exec app ls /app/data
# empty, even though ./data on the host clearly has files
Why It Happens
Several distinct configuration issues produce this exact symptom:
- The host-side path in the volume mapping is relative, and Compose resolves relative paths relative to the location of the
docker-compose.ymlfile itself — if you're running commands from a different directory, or the file structure doesn't match what the relative path assumes, it can point at an unexpected (and genuinely empty) location - A named volume declared for the same container path as a bind mount, where the named volume — often created empty by default the first time — ends up mounted in a way that masks the bind-mounted content, depending on the exact order and interaction of multiple volume declarations for overlapping paths
- The Dockerfile's own
COPYinstruction populated that directory during the image build, but the volume mount at container runtime then overlays that path with the (empty) host directory, hiding the files that were baked into the image — this is actually expected volume behavior, not a bug, but often surprises people who assumed the image's baked-in files would somehow merge with the mounted content - A typo in the container-side path in the volume mapping, meaning the volume is correctly populated, just mounted at a different path inside the container than where you're actually checking
The Fix
First, confirm the host-side path is resolving to what you actually expect, especially if it's relative:
services:
app:
volumes:
- ./data:/app/data
Relative paths like ./data resolve relative to the directory containing the docker-compose.yml file, not necessarily your current working directory when you run docker compose commands. Confirm the actual absolute path being used:
docker compose config
# shows the fully resolved configuration, including absolute paths for volumes
If you have both a named volume and a bind mount targeting related paths, check for any unintentional overlap or masking. Named volumes and bind mounts for the exact same container path conflict directly — only one wins, and it's not always obvious which one from reading the config casually:
services:
app:
volumes:
- ./data:/app/data # bind mount
- app_data:/app/data # ALSO targeting /app/data - conflict!
volumes:
app_data:
Remove the redundant or conflicting declaration, keeping only the one you actually intend for that specific path.
If the directory was populated during the image build via COPY, but you're also mounting a volume at the same path, understand that the volume mount always takes precedence at container runtime, hiding the image's baked-in content underneath it — this is standard, expected Docker volume behavior. If you need the host directory to start with the image's baked-in content, you'll need to explicitly copy it out during container startup (via an entrypoint script) rather than relying on the image build and the volume mount to somehow merge automatically, since they don't.
Double-check the container-side path exactly matches where you're actually checking for files, since a typo here means the volume is genuinely correctly mounted, just at an unexpected location:
docker compose exec app find / -maxdepth 3 -type d -name "data"
This searches for any directory named "data" within the container's filesystem, which can reveal the volume mounted at a slightly different path than expected due to a typo in the compose file.
Still Not Working?
If paths are confirmed correct and there's no conflicting named volume, check whether you're on a platform (like Docker Desktop on macOS or Windows) where file sharing needs to be explicitly enabled for the specific host directory being mounted — some Docker Desktop configurations restrict which host paths are actually allowed to be shared into containers, and a path outside the allowed/configured sharing scope can silently mount as empty rather than producing an explicit permission error, depending on the specific platform and Docker Desktop version.