Files copied in the Dockerfile are missing at runtime (anonymous volume shadows the image content)
Fixes files 'disappearing' from a container when a VOLUME instruction or -v covers the directory COPY populated. Use when the image build clearly copies files but the running container shows an empty directory. Not for .dockerignore or COPY failures.
TL;DR: A volume is mounted over the directory your Dockerfile populated, hiding the image files. Remove the VOLUME instruction (or the -v flag) covering that path, or copy the files at container start via an entrypoint script. Volumes always hide whatever was in the image at that path; that is by design.
The error
No error text; the symptom is an empty directory at runtime despite a successful COPY:
$ docker run myimage ls /app
(empty)while the Dockerfile contains COPY . /app and the build log shows it succeeding.
Fix it
Confirm a volume covers the path:
docker inspect myimage --format '{{.Config.Volumes}}'Expected: showsmap[/app:{}]or similar.- Check your run command for
-v ...:/apptoo; either source shadows the content. Fix options:
- Delete the
VOLUME /appline from the Dockerfile and rebuild (image files become visible). - Or keep the volume and seed it at runtime: entrypoint script copies defaults into /app only if empty.
- Delete the
- Rebuild and verify
docker run myimage ls /applists files.
When this applies
- Dockerfile has both COPY and VOLUME for the same path
docker run -v myvol:/appover a populated /app
When this does NOT apply
- COPY itself failed (build error, different skill)
- Files missing because of .dockerignore (build-time exclusion)
Versions
All Docker versions; VOLUME semantics are ancient and stable.
Why it happens
Mounts overlay the image filesystem. When the container starts, the volume (empty on first use) is mounted at /app, hiding the image layer's /app entirely. Named volumes get one special case: on FIRST use with an empty volume, docker copies the image content in, but anonymous volumes and subsequent runs do not.
Edge cases
- The named-volume auto-copy only happens when the volume is brand new AND the mount is a named volume;
docker run -v /app(anonymous) never copies. - Databases rely on this shadowing intentionally (VOLUME /var/lib/mysql); the bug pattern is doing it for app code or config.
docker composerecreating volumes:down -vwipes the seeded copy too.