How to mount a single file in a volume
I am trying to dockerize a PHP application. In the dockerfile, I download the archive, extract it, etc.
Everything works fine. However, if a new version gets released and I update the dockerfile, I have to reinstall the application, because the config.php gets overwritten.
So I thought I can mount the file as a volume, like I do with the database.
I tried it two ways, with a volume and a direct path.
docker-compose:
version: '2'
services:
app:
build: src
ports:
- "8080:80"
depends_on:
- mysql
volumes:
- app-conf:/var/www/html/upload
- app-conf:/var/www/html/config.php
environment:
DB_TYPE: mysql
DB_MANAGER: MysqlManager
mysql:
image: mysql:5.6
container_name: mysql
volumes:
- mysqldata:/var/lib/mysql
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD:
MYSQL_DATABASE:
MYSQL_USER:
MYSQL_PASSWORD:
volumes:
mysqldata:
app-conf:
Which results in the error:
And I tried it with a given path, as a mounted volume.
/src/docker/myapp/upload:/var/www/html/upload
/src/docker/myapp/upload:/var/www/html/config.php
However, both ways are not working. With the mounted volume, I see that upload gets created.
But then it fails with:
/var/www/html/config.php\" caused \"not a directory\"""
If I try it with
/src/docker/myapp/upload/config.php:/var/www/html/config.php
Docker creates the upload folder and then a config.php folder. Not a file.
Or is there another way to persist the config?
Solution 1:
TL;DR/Notice:
If you experience a directory being created in place of the file you are trying to mount, you have probably failed to supply a valid and absolute path. This is a common mistake with a silent and confusing failure mode.
File volumes are done this way in docker (absolute path example (can use env variables), and you need to mention the file name) :
volumes:
- /src/docker/myapp/upload:/var/www/html/upload
- /src/docker/myapp/upload/config.php:/var/www/html/config.php
You can also do:
volumes:
- ${PWD}/upload:/var/www/html/upload
- ${PWD}/upload/config.php:/var/www/html/config.php
If you fire the docker-compose from /src/docker/myapp
folder
Solution 2:
I had been suffering from a similar issue. I was trying to import my config file to my container so that I can fix it every time I need without re-building the image.
I mean I thought the below command would map $(pwd)/config.py
from Docker host to /root/app/config.py
into the container as a file.
docker run -v $(pwd)/config.py:/root/app/config.py my_docker_image
However, it always created a directory named config.py
, not a file.
while looking for clue, I found the reason(from here)
If you use -v or --volume to bind-mount a file or directory that does not yet exist on the Docker host, -v will create the endpoint for you. It is always created as a directory.
Therefore, it is always created as a directory because my docker host does not have $(pwd)/config.py
.
Even if I create config.py in docker host.
$(pwd)/config.py
just overwirte /root/app/config.py
not exporting /root/app/config.py
.
Solution 3:
Use mount (--mount
) instead volume (-v
)
More info: https://docs.docker.com/storage/bind-mounts/
Example:
Ensure /tmp/a.txt exists on docker host
docker run -it --mount type=bind,source=/tmp/a.txt,target=/root/a.txt alpine sh
Solution 4:
The way that worked for me is to use a bind
mount
version: "3.7"
services:
app:
image: app:latest
volumes:
- type: bind
source: ./sourceFile.yaml
target: /location/targetFile.yaml
Thanks mike breed for the answer over at: Mount single file from volume using docker-compose
You need to use the "long syntax" to express a bind
mount using the volumes
key: https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-3
Solution 5:
As of docker-compose file version 3.2, you can specify a volume mount of type "bind" (instead of the default type "volume") that allows you to mount a single file into the container. Search for "bind mount" in the docker-compose volume docs: https://docs.docker.com/compose/compose-file/#volumes
In my case, I was trying to mount a single ".secrets" file into my application that contained secrets for local development and testing only. In production, my application fetches these secrets from AWS instead.
If I mounted this file as a volume using the shorthand syntax:
volumes:
- ./.secrets:/data/app/.secrets
Docker would create a ".secrets" directory inside the container instead of mapping to the file outside of the container. My code would then raise an error like "IsADirectoryError: [Errno 21] Is a directory: '.secrets'".
I fixed this by using the long-hand syntax instead, specifying my secrets file using a read-only "bind" volume mount:
volumes:
- type: bind
source: ./.secrets
target: /data/app/.secrets
read_only: true
Now Docker correctly mounts my .secrets file into the container, creating a file inside the container instead of a directory.