How to mount a single file in a volume
Asked Answered
M

21

565

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?

Mainmast answered 15/2, 2017 at 11:33 Comment(2)
In my case, I simple that to "touch" an empty file before creating the container/volume. If the file didn't exist, it created a directory.Convivial
If there are contents in the container file in the image and if you touch the host file before binding the volume as a file, the image version of the file is overwritten with an empty file. Is there a way to bind the container file to a file on the host and "get out" the existing data in the container file?Ulrikeulster
S
711

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

Sawdust answered 15/2, 2017 at 22:6 Comment(17)
Like I said, if I try /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.Mainmast
In your post you wrote differently. If the file is there it should mount as file.Sawdust
docs.docker.com/engine/tutorials/dockervolumes/#/… <- should be defenitely workingSawdust
In order for this to work I had to supply an absolute path. The tip using PWD helped a lot here, thanks!Adscription
For me is creating a folder rather than the files, but I think because that file is copied later in that path is there anyway to postpone the binding?Selfreproach
if /src/docker/myapp/upload/config.php doesn't exists in the host docker will create a folder... What i'm doing is creating a script that creates an empty file before running docker run - would be ideal if I can instruct docker to do it for me via cmd line args.Luisluisa
Though it worked when the container already exist but in a fresh new instance when doing so I am getting the ERROR: Duplicate mount points: issueIncontrovertible
My solution was even better - make sure the file you're trying to mount actually exists! If it doesnt, it will throw this error since it tries to create a directory with the given file name.Christelchristen
Windows users will want to call set PWD=%CD% before calling docker-compose up to populate the PWD variable.Saccharine
use $(pwd) instead of ${PWD} on linuxCovariance
If it initially created a folder for you, make sure to stop and remove the problematic container so it can clean the folder and map the file from the host.Superannuated
If ${PWD} doesn't work. It might be you're using sudo. RefOculist
my problem is that even after ensuring that the host file exists (and mirrors the container file) I'm still getting a "not a directory" error because it was created as directory once. there must be some way to reset this, because before the host file was deleted it was working exactly with this config.Peptonize
Just use relative paths ` - ./src/foo:/etc/foo` as PWD is undefined on windows and -/ works on Linux and Windows. Paths are relative to the docker file.Torus
Works fine with relative paths ` - ./php.custom.ini:/usr/local/etc/php/conf.d/php.custom.ini`Michaelamichaele
How would the thing be easier if the docker would just refuse to start the container if the volume doesn't exist. With a helpful message. Wouldn't it? All this would be unnecessary and many directories owned by "root" under my user account would not be created. (E.g. for dev, mount "${PWD}/storage" into the container, but it didn't exist. Now it exists, owned by root, because docker creates it.)Alaniz
Here is an example why docker should not throw error when host path does exists: docker run --rm --network none -v $(pwd)/a/b/c:/a/b/c -it alpine/make mkdir -p /a/b/c/d/e/f it should create a dir (in host pwd) like a/b/c/d/e/f. You don't need to mkdir -p a/b/c before docker run. a/b/c is not created yet, so docker will not know it's a file or dir, or just not created yet, so it assumes it should be a dir (best guess, most of the time that's the case).Winburn
W
153

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.

Winter answered 3/11, 2017 at 15:19 Comment(6)
I just added a touch path/to/file in the Dockerfileso that when I mount it would still be a file (not a created dir)Bullfinch
left side of : has to be full path. It works this wayMaillot
The touch was the answer, do that in Dockerfile then your docker-compose volume mount will work correctly without creating a directoryGaddis
And don't forget to mkdir -p /path/to/file for good measure before touch /path/to/fileRakehell
Thanks! This was the issue for my case. I use './docker/uploads.ini:/usr/...' but the docker-compose file in in docker folder. So I change to './uploads.ini:/usr/...' and it works.Beep
This "answer" does not respond to the topic's question. volume is for directories. So its hacking to mount a single file is a misdirection.Rightminded
C
153

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

Christelchristen answered 1/1, 2020 at 17:20 Comment(2)
This answer is way more concise than the above and did exactly what I needed. Thank you.Shul
this doesn't work in case of file is created in container, it throws Error response from daemon: invalid mount config for type "bind": bind source path does not existCrystalcrystalline
P
103

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
Pendragon answered 17/1, 2019 at 19:35 Comment(3)
Thank you Subbu, @Jakub Juszczak this should be the accepted answer, it's the only answer that answers the question at all, and it worked for meDerrik
This does not really answer the question. volumes will work as well (because the issue is in absolute path). The only difference between --mount and -v is behavior when host part of the volume doesn't exist yet. As per your link: >If you use -v or --volume to bind-mount a file or directory that does not yet exist on the Docker host, -v creates the endpoint for you. It is always created as a directory.Zane
I do not know how to translate this into a docker-compose. 'Little help?Rhyme
L
43

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.

Lampe answered 20/3, 2020 at 21:0 Comment(2)
I have the same error also by specifying a "bind" volume mount, any source is mounted as a directoryBangui
The file still needs to be created.Bonin
K
36

All above answers are Correct.

but one thing that I found really helpful is that mounted file should exist inside docker host in advance otherwise docker will create a directory instead.

for example:

/a/file/inside/host/hostFile.txt:/a/file/inside/container/containerFile.txt

hostFile.txt should exist in advance. otherwise you will receive this error: containerFile.txt is a directory

Kimikokimitri answered 23/2, 2021 at 5:6 Comment(0)
P
32

For anyone using Windows container like me, know that you CANNOT bind or mount single files using windows container.

The following examples will fail when using Windows-based containers, as the destination of a volume or bind mount inside the container must be one of: a non-existing or empty directory; or a drive other than C:. Further, the source of a bind mount must be a local directory, not a file.

net use z: \\remotemachine\share

docker run -v z:\foo:c:\dest ...

docker run -v \\uncpath\to\directory:c:\dest ...

docker run -v c:\foo\somefile.txt:c:\dest ...

docker run -v c:\foo:c: ...

docker run -v c:\foo:c:\existing-directory-with-contents ...

It's hard to spot but it's there

Link to the Github issue regarding mapping files into Windows container

Pare answered 21/2, 2019 at 20:43 Comment(3)
Do you know if it is still the case in july 2020 ?Trilogy
@OvidiuBuligan Yes. According a comment from a docker developer on the issue, it's preferable to mount directory anyway, even on linux. They also said they cannot allow this behavior without a massive rewrite. So I really doubt we will see this in the short-medium term.Pare
We faced the same scenario. But in K8s it allowed mounting as a destination file from conigMap entry. Link to repo in case anyone is interested. github.com/ms-azure-demos/…Rogue
R
21

You can also use a relative path in your docker-compose.yml file like this (tested on Windows host, Linux container):

volumes:
    - ./test.conf:/fluentd/etc/test.conf
Raybin answered 13/6, 2019 at 21:27 Comment(4)
I tested this on Mac with version: '3.7' specified in the docker-compose.yml file with docker-compose version 1.24.1, build 4667896b, and it worked.Utoaztecan
I still get a directory instead of a file.Sciolism
@Sciolism have a look on this Answer https://mcmap.net/q/73318/-how-to-mount-a-single-file-in-a-volumeKimikokimitri
And read that line over and over. I'm consistent in trying to map .env as ./env:/app.env and forgetting the dot (so instead of ./.env:/app.env). I think because I already typed it (to specify current dir) my muscle memory does not want to add the second dot.. :-PSaville
W
5

In compose I am using a relative path and it works:

version: "3.7"
services:
    svc1: 
      volumes:
        # Current dir is parent of src
        - "./src/file.conf:/path/in/container/file.conf 

Using docker run command to bind mount a file produces:

docker: Error response from daemon: invalid mount config for type "bind": invalid mount path: 'path/file.conf' mount path must be absolute. 
See 'docker run --help'.

Apparently the only way to do this is to specify an absolute mount path like this:

docker run -it --rm --mount type=bind,source="/path/to/file.conf",target=/file.conf alpine sh

also using "%cd%" for Windows Users or "$(pwd)" for Linux Users is a way to deal with absolute paths.

see storage bind mounts

For Visual Studio Code Users make sure you are running the %cd% command in a Command Prompt Terminal, not PowerShell.

Wimer answered 30/1, 2022 at 3:26 Comment(0)
R
5

TL&DR: Docker defaults all mount points to directories. If you want to make sure your file is mounted into your docker instance, the file must already exist prior to docker-compose up and running.

Explanation:

Traditionally, the purpose of mounting a volume has always meant mounting a storage location to a directory. However, that is now changing with the ability to now just share files. From a programmer's perspective, its hard to know whether a "/tmp/something" is a file or a directory. Docker, therefore, assumes all mount points are first a directory unless the file already exists.

In your example if you want a file to be mounted, make sure it already exists on the host system. When Docker sees that this entry already exists it will mount the file into the docker environment.

Riverside answered 14/11, 2022 at 3:1 Comment(0)
I
4

Be carefull if you use docker context to operate with docker on remote virtual machine. In this case the file you trying to mount will certanly not exist on target VM. Thus you not existed file will be mount as folder.

Iodous answered 25/9, 2022 at 20:47 Comment(4)
This is a great commentRiverside
This is my situation - but what should I do to solve the issue?Oldline
@david-montgomery, you should change your approach or move the mounted files to the remote machine or place them to the docker imageIodous
Yeah I was doing that already, I misunderstood what this question was saying @IodousOldline
E
3

You can mount files or directories/folders it all depends on Source file or directory. And also you need to provide full path or if you are not sure you can use PWD. Here is a simple working example.

In this example, I am mounting env-commands file which already exists in my working directory

$ docker run  --rm -it -v ${PWD}/env-commands:/env-commands aravindgv/eosdt:1.0.5 /bin/bash -c "cat /env-commands"
Etienne answered 8/6, 2020 at 10:33 Comment(0)
V
1

I had the same issue, docker-compose was creating a directory instead of a file, then crashing mid-way.

what i did :

  1. run the container without mapping the file

  2. copy the config file to the host location :

    docker cp containername:/var/www/html/config.php ./config.php

  3. remove the container (docker-compose down)

  4. put the mapping back and remount up the container

docker compose will find the config file, and will map that instead of trying to create a directory.

Viaticum answered 8/8, 2020 at 2:25 Comment(0)
D
1

2022 answer, on Mac with Minikube/Hyperkit docker and Docker Compose

Since I'm not using Docker Desktop any longer, I've experienced numerous issues similar to "docker in docker (dind)" paradigm with minikube...

  1. mount minikube
  2. use absolute path

e.g., easiest way was to mount the exact home path...

minikube mount $HOME:/Users/<you>

... keeps running...

docker-compose.yaml

volumes:
      - /Users/<you>/path/to/file.yaml:/somedir/file.yaml
Delimitate answered 10/5, 2022 at 12:14 Comment(0)
R
0

For me, the issue was that I had a broken symbolic link on the file I was trying to mount into the container

Restriction answered 27/7, 2018 at 13:50 Comment(0)
V
0

I have same issue on my Windows 8.1

It turned out that it was due to case-sensitivity of path. I called docker-compose up from directory cd /c/users/alex/ and inside container a file was turned into directory.

But when I did cd /c/Users/alex/ (not Users capitalized) and called docker-compose up from there, it worked.

In my system both Users dir and Alex dir are capitalized, though it seems like only Users dir matter.

Veliger answered 13/10, 2018 at 13:56 Comment(0)
L
0

In windows, if you need the a ${PWD} env variable in your docker-compose.yml you can creat a .env file in the same directory as your docker-compose.yml file then insert manualy the location of your folder.

CMD (pwd_var.bat) :

echo PWD=%cd% >> .env

Powershell (pwd_var.ps1) :

$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'; echo "PWD=$(get-location).path" >> .env

There is more good features hear for docker-compose .env variables: https://docs.docker.com/compose/reference/envvars/ especially for the COMPOSE_CONVERT_WINDOWS_PATHS env variable that allow docker compose to accept windows path with baskslash "\".

When you want to share a file on windows, the file must exist before sharing it with the container.

Legra answered 28/1, 2019 at 16:36 Comment(1)
You can not use PowerShell with docker, it creates a vendor lock-in and hurts the community...Juliannajulianne
P
0

Maybe this helps someone.

I had this problem and tried everything. Volume bindings looked well and even if I mounted directory (not files), I had the file names in the mounted directory correctly but mounted as dirs.

I tried to re-enable shared drives and Docker complained the firewall is active.

After disabling the firewall all was working fine.

Percent answered 12/9, 2019 at 14:56 Comment(1)
Thank you for that! Your post made me realize that my selinux was set to enforcing and that was causing all of my troubles. Don't know why you were down-voted ¯_(ツ)_/¯Pectinate
O
0

This also seems to happen if you forget to actually include the file you want to mount in the source dir. I learned the hard way. No error message such as 'file not present', instead creates a directory in the destination.

Orthodox answered 9/8, 2022 at 21:25 Comment(0)
D
-1

I had the same issue on Windows, Docker 18.06.1-ce-win73 (19507).

Removing and re-adding the shared drive via the Docker settings panel and everything worked again.

Desiccate answered 24/10, 2018 at 14:43 Comment(0)
O
-1

For myself, the issue was that I specified the incorrect source file.

Here's an example:

 robert ❱ ~ ❱ docker run --rm -it -v "${PWD}/non-existant:/root/destination" -w /root --entrypoint /usr/bin/ls ubuntu -lad destination
drwxr-xr-x 2 root root 64 Mar 29 05:54 destination
 robert ❱ ~ ❱ touch exists
 robert ❱ ~ ❱ docker run --rm -it -v "${PWD}/exists:/root/destination" -w /root --entrypoint /usr/bin/ls ubuntu -lad destination
-rw-r--r-- 1 root root 0 Mar 29 05:58 destination
 robert ❱ ~ ❱

TL;DR - It could be your spelling as it was for me.

Overset answered 29/3, 2022 at 6:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.