I have a Volume shared amongst several containers in my Docker Compose setup. It looks like this in compose.yml
:
php:
volumes:
- type: bind
source: ./apps
target: /var/www
I have a bash script which interacts with files in the shared Volume. It's called setup.sh
. It's included in php
's Dockerfile.
What I would like to do is have it run as a RUN
command in the Dockerfile:
COPY dockerfiles/php/setup.sh /usr/local/bin/setup.sh
RUN chmod +x /usr/local/bin/setup.sh
RUN /usr/local/bin/setup.sh
However, this doesn't work, because the Volume is not mounted until the very last command—the entrypoint.
This does work:
CMD sh -c /usr/local/bin/setup.sh && /usr/sbin/php-fpm && tail -f /dev/null
However, the issue with this is that when I spin up the containers with docker compose
, it shows as Started
, but the script takes a minute or so to run before PHP-FPM is up. There is no easy way to tell when the script is done. So I'd prefer to run the script before the entrypoint so it's just waiting on PHP-FPM to start up (which does not take long).
Is there any way to do that?
How do you interact with a shared Volume created by Docker Compose in a Dockerfile?
You can't. It doesn't exist yet. It's possible the image that's being built will be run multiple times, on different machines, with different mounted volumes.
The setup you describe sounds like you're trying to inject the entire application source code through a bind mount. This is often used to try to make Docker emulate a local development environment. A typical Docker setup is to have immutable, self-contained images, however: all of the application code is contained in the image (and compiled if required), and you can run the image without the mount you show.
If you COPY
the application code into the image, then it's present, and you can RUN
your build command.
COPY ./apps /var/www/
COPY dockerfiles/php/setup.sh /usr/local/bin/setup.sh
RUN setup.sh
CMD ["php-fpm", "-F"]
If you're in a different situation – maybe these files are some sort of installation-specific data – then you must run this at container startup time; you can't run it earlier. In that case, I prefer an entrypoint wrapper script as a little bit cleaner than the extended CMD
you show. That script can look like
#!/bin/sh
# run the setup script
setup.sh || exit 1
# switch to the main container command
exec "$@"
Check the script into source control as executable, COPY
it into your image, and make that script be the image's ENTRYPOINT
. You must use JSON-array syntax for ENTRYPOINT
. The last line of the script runs the CMD
, which may be any syntax.
COPY entrypoint.sh /usr/local/bin/
ENTRYPOINT ["entrypoint.sh"]
CMD ["php-fpm", "-F"]
In both cases I'm using the php-fpm -F
option to run the interpreter in the foreground. Avoid using tail(1) to "keep the container alive".
With the ENTRYPOINT
approach it's possible to override the CMD
to do something else. For example, if you
docker run --rm your-image \
ls -l /var/www/extra_content
or similar, you can run a temporary container (--rm
), running ls
as the main container process, and verify what got created by the setup script.