0

My main question is: with docker compose on Docker Desktop with WSL 2, when using a bind mount where the host path is a relative path, where and what is the actual host path on the WSL distro used by the docker desktop backend?

Sections below help understanding the context, what I searched and understood, and detail my questions.

Actual source issue

I have a docker compose configuration and a container with an Angular application. The content of the project is located on the Windows file system, and the docker compose uses a bind mount with a relative path.

The container does get the initial content, but it seems to never be updated: no live reload, no updated content when (hard-)refreshing the browser page.

Note that this is with Docker Desktop on Windows, after switching from the Hyper-V backend to the WSL 2 one. With Hyper-V, everything worked as expected, it was just much slower (as they state in the documentation).

My investigations

I really searched a lot... and got quite mad since I took many paths and always got badly stuck.

  • I tried to run a WSL shell inside docker-desktop-data which was running: it fails, returning immediately, and the process exit code is just the generic 1 for error. Btw, doing the same for docker-desktop does work.
  • I tried to find the content using the Windows explorer in the network folder \\wsl$\docker-desktop-data. There I could finally see the persisted volumes, but couldn't find any folder that would correspond to the bind mount
  • I couldn't find the information about what internal path the docker engine exposes to the container (which is what my question's title targets). I could see the WSL distro had mounted paths corresponding to the Windows file system drives, but it doesn't mean that this is what it uses (and in fact, there I could see my content updated, while, as I said, nothing was updated when refreshing the browser hence my hypothesis that the content is lying elsewhere)
  • I discovered the existence of the ext4.vhdx files located under C:\Users\<username>\AppData\Local\Docker\wsl\{data,distro}. I tried to mount it, but as expected it refused since it was already mounted. However I couldn't find any info about where to see the mounted content. It might by this \\wsl$\docker-desktop[-data] thing, but no doc, nor any command (I tried PowerShell's Get-VHD which just told me it's "attached") could tell me how to get that info.

And probably a few other things which I don't even remember and that were probably already too far from the initial topic.

I also checked the official Docker documentation pages with no luck finding these details.

My next step would be to read the source code of the backends directly...

My expectations

The real, pragmatic outcome for me is to be able to have the container interact with the local Windows file system content, instead of what seems to be a copy, which I can't locate.

As a bonus, I would love to understand what paths are involved, and what mechanisms are used to bind all of that (e.g. with the working Hyper-V backend, I expect a Hyper-V specific mechanism to have been used to make it possible for the virtualized docker process to expose a Linux path to the container which eventually was just a sort of hard link / shared folder - conceptually speaking - to the Windows file system folder). So, in docker compose's configuration I see a host <=> container path mapping, but being on Windows there's really a windows host <=> linux host <=> container mapping and it's that windows host <=> linux host mapping which seems to behave differently between Hyper-V and WSL 2, with me being unable to locate the linux host path.

If all of that could be backed by links to documentations (preferably reference/official) it would make it PERFECT.

But, really, getting a solution to the first point would already be extremely helpful.

Yannick Meine
  • 499
  • 5
  • 11

2 Answers2

1

My question to confirm what path is actually used by Docker as the host path on WSL 2 does remain, but more as a curiosity now rather than a necessity (explanations below).

However, I guess more and more that it is translating the Windows path to /mnt/<my-absolute-path-here> in the docker-desktop distro.

Disclaimer & context recap

All the information below is not answering my initial question, but hopefully it might help others on the topics that were involved during my investigations. Sorry if it is technically off-topic with regards to the question, but while I started to give context within my initial description, I also accidentally introduced those topics as well.

Now, back to the initial issue I was trying to solve: an Angular app served by ng serve was not showing files with their updated content, be it through a therefore non-working live-reload or even through a browser refresh.

My first validation: a simple file server

I therefore built the smallest server I could imagine and ran it inside Docker with a bind mount on some folder where I would update files that I serve. When calling again the server it would give me the updated files.

First conclusion: it's not a matter of file content not being updated, it's a matter of change detection and cache.

Hypothesis here: while ng serve live-reload feature would obliviously not work if file change detection does not work, I suspect the latter is also used as the source of truth for the cached build, and that therefore reloading the browser page can't force rebuilding with the updated file content.

My second validation: run chokidar

So I built another small server, returning me the list of events caught by chokidar on a folder exposed through a bind mount.

It didn't detect any file changes when those were updated on the Windows side. However, if I changed the files from a WSL 2 shell, it would get detected.

Second conclusion: while both Windows and WSL 2 eventually target the same files, the APIs for those files are not working the same way depending on what side interacts with them.

Workarounds and next investigations

For those who, like me, wanted to make this work for an Angular application, or use any file change detection library, here is one workaround: use polling.

  • for Angular: use the --poll option, check serve and build commands documentation
  • for chokidar or other libraries: activate any polling option (chokidar has one here), otherwise you're out of luck regarding that workaround

Now, having this work efficiently without a workaround remains one of my objectives, so I'll try to see how to keep editing content on the Windows side while keeping the same level of features as if the application were running on the Windows side as well.

References

A big thank to that answer in windows subsystem for linux - WSL filesystem miscoherency - Super User. It explains the dichotomy of the file systems between Windows and WSL 2.

While it remains unclear what works or not yet, and what is planned, it does give a reasonable explanation to the question of why file watching is not working.

Now, some examples of why it's not easy to figure out what to expect:

  • modification date is properly propagated between systems
  • WSL 2 (inotify I guess) does not catch any changes when the Windows side modifies content
  • however Windows does catch changes when the WSL 2 side modifies content

EDIT 2022/04/04

Well, I can confirm that it is linked to inotify, here are some links:

Yannick Meine
  • 499
  • 5
  • 11
  • 1
    While it's impossible to truly know the reason for downvotes, I've noticed a recent trend that Docker Desktop questions are often downvoted a couple of times. This may be because some consider [general Docker support](https://meta.stackoverflow.com/questions/353822/should-the-docker-tag-discourage-general-support-questions) to be off-topic. I'm not sure I agree personally. I think Docker Desktop for Windows is pretty much *purely* a developer tool (unlike, say, WSL), but perhaps I'm wrong. – NotTheDr01ds Apr 04 '22 at 02:10
  • Thanks @NotTheDr01ds, for giving some pointers (and editing my answer accordingly). I indeed hesitated between two issues: did I post my question in the wrong place (for instance the WSL related question I linked is indeed on SuperUser), or was my question too unclear? While I would easily agree on the latter, the former is much harder to assess, since I don't even know how hard and clear are the boundaries defined. Then I was right to hesitate posting on StackOverflow. I'll still post an answer to my own question (I think I'm close), to hopefully help anyone who would face the same issues. – Yannick Meine Apr 04 '22 at 10:14
1

So, while I still couldn't find a perfect source of info (like official documentation or even source code), I could find some data which confirms some of the information or hypothesis made earlier.

The strategy

Remembering that Docker is a tool that builds around core Linux APIs, like other container systems, I started to look at namespaces, and more specifically mount namespaces.

The procedure to get the information

Therefore, on a WSL 2 shell instance on the docker-desktop distro (wsl -d docker-desktop), the procedure is as follows:

  • find the process ID corresponding to the container for which you want to get information about the mounts: use ps for that. We'll name that value <pid> below.
  • then show the content of /proc/<pid>/mountinfo: you can use cat for that
  • each line corresponds to a mount and contains all the needed information. Find the relevant line for your use case. Examples:
    • for Portainer with standard installation, which uses a named volume: 975 931 8:32 /version-pack-data/community/docker/volumes/portainer_data/_data /data rw,relatime master:3 - ext4 /dev/sdc rw,discard,errors=remount-ro,data=ordered
    • for my use case with a bind mount: 1092 1070 0:56 /whatever/path/on/the/c/drive /container/path rw,noatime - 9p C:\134 rw,dirsync,aname=drvfs;path=C:\;uid=0;gid=0;metadata;symlinkroot=/mnt/host,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8

Interpretation of the information

The named volume use case (applied to Portainer)

975 931 8:32 /version-pack-data/community/docker/volumes/portainer_data/_data /data rw,relatime master:3 - ext4 /dev/sdc rw,discard,errors=remount-ro,data=ordered contains the following relevant information:

  • /version-pack-data/community/docker/volumes/portainer_data/_data is the path of the volume data on the Linux host, relatively to:
  • /dev/sdc, which points to the docker-desktop-data distro (you can check that using findmnt -S /dev/sdc, which gives us something like /mnt/host/wsl/docker-desktop-data)
  • it is exposed on /data within the container, as configured when creating it

So eventually, it maps /data to /version-pack-data/community/docker/volumes/portainer_data/_data on device /dev/sdc, which can be accessed as well from /mnt/host/wsl/docker-desktop-data/version-pack-data/community/docker/volumes/portainer_data/_data.

However I suspect the mount namespace to be using the device directly, not its mounted path (which I provided only to "prove" it corresponds to the docker-desktop-data distro).

The bind mount use case

1092 1070 0:56 /whatever/path/on/the/c/drive /container/path rw,noatime - 9p C:\134 rw,dirsync,aname=drvfs;path=C:\;uid=0;gid=0;metadata;symlinkroot=/mnt/host,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8:

  • /container/path inside the container points to...
  • /whatever/path/on/the/c/drive on the host, through the filesystem of type...
  • 9p, which contains the following parameters:
    • path=C:/: so /whatever/path/on/the/c/drive is relative to the C:/ Windows drive
    • symlinkroot=/mnt/host: that C:/ drive is mounted in /mnt/host, implicitly under /mnt/host/c

So eventually, the host path is again directly consumed from the filesystem, but it has a correspondence on /mnt/host/c/whatever/path/on/the/c/drive.

References

Yannick Meine
  • 499
  • 5
  • 11