Docker Container Can't See Host Files? Anonymous Volume Overrides Bind Mount
Encountered this issue while deploying a WordPress site for a client. Here's the root cause and solution.
TL;DR
The VOLUME declaration in an image's Dockerfile creates anonymous volumes with higher mount priority than docker-compose.yml bind mounts. Solution: Stop container → Delete anonymous volume → Restart.
Problem
After CI deployment, new static files or PHP code don't exist inside the container:
assets/images/logo.pngexists on host, missing in container → Logo doesn't displayinc/setup.phphas new filter code, container has old version → Filter doesn't work- Files updated after
git pull, container still has old content
Root Cause
WordPress official image's Dockerfile includes:
VOLUME /var/www/html
Even if your docker-compose.yml configures bind mount:
volumes:
- ./wordpress/wp-content:/var/www/html/wp-content
Docker still creates an anonymous volume for the VOLUME declared path. Anonymous volumes have higher mount priority than bind mounts, causing:
/var/www/htmlis taken over by anonymous volume- Your bind mount targets
/var/www/html/wp-content - But the anonymous volume already "occupies" the parent directory, bind mount gets overridden
Verify with docker inspect:
docker inspect prod_wordpress --format '{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
Output similar to:
volume: /var/lib/docker/volumes/wp-prod_wp_html/_data -> /var/www/html # Anonymous volume!
bind: /var/www/wp-prod/wordpress/wp-content -> /var/www/html/wp-content
Solution
# 1. Stop containers
cd /var/www/wp-prod && docker compose down
# 2. Delete anonymous volume
docker volume rm wp-prod_wp_html
# 3. Restart
docker compose up -d
Verification
docker inspect prod_wordpress --format '{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
Should only show bind mount, no anonymous volume:
bind: /var/www/wp-prod/wordpress/wp-content -> /var/www/html/wp-content
Prevention
When using bind mount deployment, check if the image declares VOLUME. If declared:
- Before first start, confirm no residual anonymous volumes exist
- Or modify docker-compose.yml to make bind mount path match VOLUME path (mount to same level)
After fixing, git pull updates are automatically read by the container—no docker cp or container restart needed.
Important
- Backup data before deleting anonymous volumes or ensure it can be restored via git pull
- Test in staging environment before production operations
- If container has critical data, backup with
docker cpfirst