0

As part of a bigger setup, I have a multi-stage Docker build with Docker Compose that has a builder container with some data in /var/www. When I build the second stage container called application my Dockerfile has these instructions

ARG APPLICATION_BUILDER

FROM $APPLICATION_BUILDER as builder

FROM busybox

COPY --from=builder --chown=www-data:www-data /var/www /var/www

VOLUME /var/www

ENTRYPOINT ["tail"]
CMD ["-f", "/dev/null"]

The builder Dockerfile is:

FROM ubuntu:16.04

ARG APPLICATION_SOURCE_FOLDER

COPY --chown=www-data:www-data ${APPLICATION_SOURCE_FOLDER} /var/www

When I start the builder container it has all the data in /var/www as expected. However, when I start the application container using: docker run -it <IMAGE> it contains nothing in /var/www

If I remove the VOLUME /var/www line in the Dockerfile then the data appears as expected. But I don't understand why the VOLUME instruction would make the data disappear, I thought it is meant to export the contents of /var/www/ as a volume?

Do I need to specify a -v when I do docker run in addition to the VOLUME instruction?

kos
  • 1,770
  • 2
  • 19
  • 41
  • Can you include a larger subset of the Dockerfile that provides a [mcve]? In particular, does the `builder` container also have a `VOLUME`? That declaration is subtle and usually unnecessary. – David Maze Jan 07 '20 at 21:23
  • Thank you @DavidMaze, I've extended the example. There is no `VOLUME` in the `builder` Dockerfile – kos Jan 07 '20 at 22:07
  • Is that your entire `docker run` command and how are you checking the contents of `/var/www`. Show your complete workflow from Dockerfile to ls command output. – BMitch Jan 08 '20 at 11:51
  • Also include the entire workflow to test the builder image. Include details like build args being passed to the build command. – BMitch Jan 08 '20 at 11:56

2 Answers2

1

The VOLUME directive does not "export the contents" of a directory. It says "create a volume and mount it on this path". So when you write:

COPY --from=builder --chown=www-data:www-data /var/www /var/www
VOLUME /var/www

You are saying:

  • First, copy a bunch of files into /var/www
  • Next, mount an empty volume on /var/www

The volume mount happens both at build time (affecting any subsequent RUN statements) and at runtime (affecting the environment you see with docker run).

If you remove the VOLUME directive from your Dockerfile and then run your image like this:

docker run -v /var/www yourimage ...

Then Docker will create a new volume and copy the contents of /var/www into it before mounting it.

Similarly, if you have a named volume, you can do the same thing:

docker run -v myvolume:/var/www yourimage ...

Docker will only perform this automatic copy operation if the volume you are mounting is empty. If the volume has existing content, it will simply be mounted on the specified path, hiding anything underneath that mountpoint.


For example...

I have an image named kos that has a few files in /var/www. We can see them by running ls /var/www:

bash-5.0$ docker run kos ls /var/www
index.html
testing.jpg

If I mount an anonymous volume on /var/www with docker run -v, we can see that it gets populated with the content from the underlying /var/www directory:

bash-5.0$ docker run -v /var/www kos ls /var/www
index.html
testing.jpg

We see the same behavior if we create a named volume and mount it on /var/www:

bash-5.0$ docker volume create kos_testing
kos_testing
bash-5.0$ docker run -v kos_testing:/var/www kos ls /var/www
index.html
testing.jpg
bash-5.0$
larsks
  • 277,717
  • 41
  • 399
  • 399
  • I get the same problem with the named/anonymous volumes if I specify them as part of `docker run` - the contents of `/var/www` get cleared. – kos Jan 08 '20 at 02:45
  • I've updated my answer to illustrate what I was talking about. Let me know if you are seeing different behavior. – larsks Jan 08 '20 at 03:02
  • "mount an empty volume" is not accurate. "define this path as a volume" would be better since the path is not emptied in any way by the volume step. – BMitch Jan 08 '20 at 11:54
  • The `VOLUME` keyword causes Docker to perform a bind mount of an empty, anonymous volume on the given path both at build time when the `VOLUME` keyword is encountered and at runtime, when you start a container. I think describing this as "mounting an empty volume" is accurate. – larsks Jan 08 '20 at 11:58
  • Anonymous volumes are not empty, they get initialized to the contents of the image, same as a named volume. You even showed that in your example. – BMitch Jan 09 '20 at 23:23
  • This contains more detail than I can fit here, but still doesn't explain the OP's issue. https://stackoverflow.com/a/55516433/596285 – BMitch Jan 09 '20 at 23:28
  • @BMitch it's almost like you're quoting my answer: "Then Docker will create a new volume and copy the contents of /var/www into it before mounting it...". Docker creates an (empty) volume, then copies in the underlying files, then mounts it. I guess we are arguing semantics? I think it's pretty clear the answer here agrees exactly with what you just said... – larsks Jan 09 '20 at 23:41
  • If we're agreeing that the directory will be populated in the resulting container, then I'm confused how this answers that the directory contents are empty when the volume line is in the image. The volume line should have no impact to the directory listing in the container. – BMitch Jan 10 '20 at 01:16
-2

I suspect what is happening is that, when you specify the VOLUME, it's essentially overwriting what you previously copied, with an empty volume mount.

You don't want to remove the volume, because it looks like you want this data persisted. So what you can do, is perhaps switching the instructions.

Declare your volume first, and then copy your data into your volume.

alex067
  • 3,159
  • 1
  • 11
  • 17
  • I don't think that works either - from the docs https://docs.docker.com/engine/reference/builder/#volume `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.` – kos Jan 08 '20 at 02:46