[BUG] COPY after VOLUME should be ignored
Contributing guidelines
- [X] I've read the contributing guidelines and wholeheartedly agree
I've found a bug and checked that ...
- [X] ... the documentation does not mention anything about my problem
- [x] ... there are no open or closed issues that are related to my problem
Description
According to the dockefile VOLUME documentation:
Changing the volume from within the Dockerfile: If any build steps change the data within the volume after it has been declared, those changes will be discarded.
However when we call COPY after using VOLUME the file is still copied.
Not sure where the bug is: Is the implementation wrong? Or is the documentation wrong?
Expected behaviour
COPY after VOLUME should be ignored
Actual behaviour
COPY after VOLUME is not ignored
Buildx version
github.com/docker/buildx v0.10.5 86bdced7766639d56baa4c7c449a4f6468490f87
Docker info
No response
Builders list
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default * docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
desktop-linux error
Cannot load builder desktop-linux: protocol not available
Configuration
Dockerfile:
FROM debian:bookworm-slim
RUN mkdir /content
COPY file_a.txt /content/file_a.txt
VOLUME /content
# [BUG] Should be ignored (according to the docs)
COPY file_b.txt /content/file_b.txt
Commands:
-
docker build -t test . -
docker run -dt --name test-container test -
docker exec -it test-container
Then, from inside the container:
-
ls content/
Notice both file_a and file_b show up. To my understanding file_b should have been ignored.
Build logs
No response
Additional info
No response
I did try to create a volume: docker volume create my-vol and then have spin up 2 different containers (using different images) that both COPY a file to the same volume:
docker run --name container-1 -v my-vol:/content -dt my-image:0.0.1
docker run --name container-2 -v my-vol:/content -dt my-image:0.0.2 # Same as my-image:0.0.1, just COPYies a different file to distinguish
In that case, the documentation holds true and only container-1 is able to COPY files into the my-vol. Any other container afterwards has their COPY instructions (to the same volume) ignored
I think this is the behaviour that the VOLUME instruction tries to replicate, but at the "Dockerfile level" (since the actual anonymous volume is only created when docker run is executed)
If my understanding is correct, then this description should be updated to say that:
- if the
VOLUMEpath is mounted to a pre-existing volume, and that volume is not empty, then the container won't initialize the volume with data, only mount it - if the
VOLUMEpath is mounted to a pre-existing volume and that volume is empty, the container will mount the existing volume and copy the data to the volume - the order in which you populate the data to the
VOLUMEpath in the Dockerfile doesn't matter (you can declareVOLUMEand then copy files into it, what matters is when/if that volume is empty when the container starts)
If it is just a documentation mistake, I think we can just remove the line that says:
Changing the volume from within the Dockerfile: If any build steps change the data within the volume after it has been declared, those changes will be discarded.
I suggest this because when I was browsing the documentaion, I didn't make any assumptions about what happens before or after declaring a VOLUME.
It was only after I read that paragraph that I got confused, therefore I am assuming (maybe wrongly) others will have a similar experience.
Another point: The documentation mentions "any build steps (that) change the data within the volume". COPY is one of those build steps, but I am not sure if there are any other build steps that can change the volume data.
I think this may be related to a change in behavior between BuildKit and the classic builder; with the classic builder, VOLUME statements would be applied during build, which means that for every RUN, an (anonymous / ephemeral) volume would be mounted, so changes made in the volume path would be saved to the volume that's attached, and not become part of the image's filesystem.
With BuildKit (now the default), VOLUME statements are "metadata-only" changes; the volume definition is added to the image's config, but no volumes are applied during the build, so changes to paths defined as a volume end up in the image's filesystem (but when running the image, a volume will be attached at the given location).
I'm not 100% sure what the behavior is for COPY statements with the classic builder (as COPY may not be running a container, and therefore not attaching a volume).