Using volumes on Microsoft Windows
Estimated reading time: 14 minutes
A Docker container’s runtime data is either held in RAM or on the container’s file system. By default all files created inside a container are stored in the writable layer of the container. This means that the data doesn’t persist when the container no longer exists, and it can be difficult to get the data out of the container if another process needs it.
Docker has two options for containers to store their files at locations outside the container, so that the files are persisted even after the container stops: Bind mounts and Volumes.
Prerequisites
In order to use Bind mounts and Volumes, the file or directory to mount must be present in the container’s docker image, and it must be advertised using the Dockerfile’s VOLUME command.
Here is an example of a Dockerfile creating an empty directory and advertising that directory to be used as a Bind mount or Volume in the resulting image:
# escape=`
FROM mcr.microsoft.com/windows/nanoserver
RUN MKDIR C:\Temp # Create empty directory
VOLUME C:\Temp # Advertise directory for future Bind mount or Volume
If from an image advertising a volume a container is created without specifying a matching Bind mount or Volume, the corresponding file or directory will be accessed at runtime as locally stored file or directory in the container’s runtime storage space, i.e. the writable container layer. So, the container will then behave as if no volume had been declared.
Two kinds of mount options
Two different mount options exist to add Bind mounts and Volumes to a container:
--mount
--volume
, or-v
The --mount
option syntax is extensible and, thus, more flexible, while the --volume
option provides a concise syntax for standard mounts.
--mount
syntax
The --mount
syntax comprises of multiple <key>=<value>
pairs, separated by commas. The list of keys may be augmented by external volume providers, so the --mount
option provides greater flexibility than the abridged --volume
syntax.
These are the standard keys available in Docker for Windows:
keyword | description |
---|---|
type |
Mount type. Can be bind , volume or npipe . |
source |
Mount source. May be specified as source or src . For details see subsequent sections. |
destination |
Mount destination. The absolute path where the file or directory is mounted in the container. Can be specified as destination , dst , or target . |
readonly |
If present, causes the Bind mount or Volume to be mounted into the container as read-only. |
--volume
syntax
The --volume
syntax is more concise. It determines the mount type automatically and doesn’t support volume drivers. It comprises of a triplet of three values, separated by a colon:
part | description |
---|---|
source |
Volume name or path to mount the Bind mount to. For anonymous Volumes, this field is omitted. |
destination |
The absolute path where the file or directory is mounted in the container. |
ro |
If present, causes the Bind mount or Volume to be mounted into the container as read-only. |
Storage Locations
In Docker for Windows, containers may store runtime data in up to four different locations:
- The container’s runtime storage space itself
- The Microsoft Windows host file system
- A special Managed Volume storage area
- If the Docker client is running on WSL 2: Another WSL 2 Linux distribution’s file system
Store runtime data in the container’s file system storage area
Any write access to a Docker container’s virtual hard drive that won’t be redirected by any of the subsequent techniques is performed in the container’s storage space, thereby increasing the container’s writable layer.
The data stored in the container’s file system storage area is accessible by and visible to the container itself only.
Container instances are stateless. If the container is restarted, crashes, or stops, all of its state is lost. This includes runtime data stored in the container’s file system, which is then deleted, too. To persist state beyond the lifetime of the container, you must use a Bind mount or a Volume, as described below.
Bind mount: Store runtime data in the Windows host file system
A Docker container’s read/write access to an advertised file or directory can be redirected to a directory in the Docker client’s host file system. This type of redirection is called a Bind mount.
Bind-mounted runtime data is persisted beyond the lifetime of a Docker container instance.
A Bind mount is specified by three parameters:
- The absolute path to the directory in the host file system
- The advertised absolute directory in the Docker container
- Optionally, a file access flag for read-only access
Bind mount examples:
docker create --mount type=bind,src=D:\Data,dst=C:\Temp <IMAGE>
docker create -v D:\Data:C:\Temp <IMAGE>
docker create --mount type=bind,src=D:\Data,dst=C:\Temp,readonly <IMAGE>
docker create -v D:\Data:C:\Temp:ro <IMAGE>
Bind-mount into a non-empty directory on the container
If you Bind-mount into a non-empty directory on the container, the directory’s existing contents are obscured by the Bind mount. This can be beneficial, such as when you want to test a new version of your application without building a new image. This behavior differs from that of a Volume.
Volume: Store runtime data in a special Managed Volume storage area
A Volume is a file or directory provided by the Docker client. On local Docker for Windows installations, the Volume storage area is located in the Windows host’s C:\ProgramData\Docker\volumes\
directory by default.
Runtime data stored in a Volume is persisted beyond the lifetime of a Docker container instance.
Docker Volumes can be created and given a name. So, they may be accessed and shared by different Docker containers. Alternatively, anonymous Docker Volumes can be created on-the-fly when creating the container. These anonymous Volumes can only be accessed by their corresponding container.
The --mount
syntax for Volumes accepts two additional <key>=<value>
pairs:
keyword | description |
---|---|
volume-driver |
If present, specifies the storage driver to use for the volume. Defaults to local . |
volume-opt |
Applies to Volumes only. Can be specified more than once. Takes a key-value pair consisting of the option name and its value. |
Escape values from outer CSV parser
If your volume driver accepts a comma-separated list as an option, you must escape the value from the outer CSV parser. To escape a
volume-opt
, surround it with double quotes ("
) and surround the entire mount parameter with single quotes ('
):docker service create ` --mount 'type=volume,src=<VOLUME_NAME>,dst=<CONTAINER_PATH>,volume-driver=<SOME_DRIVER>,volume-opt=type=<SOME_TYPE>,"volume-opt=params=a=x,b=y,c=z"'` --name myservice ` <IMAGE>
The --volume
syntax for Volumes is specified by these parameters:
- The volume name. May be omitted if the volume is to be created on-the-fly for a single container.
- The advertised absolute directory in the Docker container.
- Optionally, a file access flag:
ro
, for read-only access.
When using Volumes with services, only --mount
is supported.
Volume examples:
Creating and using a named volume:
docker volume create myvolume
docker create --mount type=volume,src=myvolume,dst=C:\Temp --name c1 <IMAGE_1>
docker create --mount type=volume,src=myvolume,dst=C:\Temp --name c2 <IMAGE_2>
docker volume create myvolume
docker create --volume myvolume:dst=C:\Temp --name c1 <IMAGE_1>
docker create --volume myvolume:dst=C:\Temp --name c2 <IMAGE_2>
Creating and using anonymous volumes:
docker create --mount type=volume,dst=C:\Temp <IMAGE>
docker create -v C:\Temp <IMAGE>
Mount more than a single volume on a container
You can mount more than one volume on a container, provided these volumes are all advertised in the image, like in this example:
# escape=`
FROM mcr.microsoft.com/windows/nanoserver
RUN MKDIR C:\Temp # Create empty directory
VOLUME C:\Temp # Advertise created directory for future Bind mount or Volume
VOLUME C:\inetpub\wwwroot # Advertise existing directory for future Bind mount or Volume
docker create --mount type=volume,dst=C:\Temp --mount type=volume,dst=C:\inetpub\wwwroot,readonly <IMAGE>
docker create -v C:\Temp -v C:\inetpub\wwwroot:ro <IMAGE>
Mount a Volume into a non-empty directory on the container
If you mount a Volume into a non-empty directory on the container, the directory’s existing contents are copied to the Volume share on the host. The container then mounts and uses the volume, and other containers which use the volume also have access to the pre-populated content.
Remove unused volumes
There are two types of volumes to consider:
-
Named Volumes
Named Volumes persist until they get manually deleted.
-
Anonymous Volumes
Anonymous Volumes persist until the corresponding container instance gets removed.
To remove an unused Named Volume from the Managed Volumes storage area, call docker container rm <VOLUME_NAME>
, e.g.
docker volume rm myvolume
To remove an anonymous volume, remove the corresponding Docker container instance.
To remove all unused Named Volumes not referenced by any containers from the Managed Volumes storage area, call
docker volume prune
Bind mount and Volumes handling in WSL 2
While Windows containers run in the host Windows OS, Linux containers run in a dedicated Linux distribution within the WSL 2 VM. So, additional considerations must be taken into account for Linux images in regard to Bind mounts and Volumes:
Docker for Windows installs two Linux distros in the WSL 2 engine: docker-desktop
and docker-desktop-data
.
docker-desktop
hosts and runs the Docker Linux containersdocker-desktop-data
hosts the Docker Linux images and Volume storage spaces.
Both WSL 2 instances can be displayed using the wsl -l -v
command.
The Docker WSL 2 instances get started when Docker for Windows is switched to using Linux containers. So, Linux containers are not available until you switch to using Linux containers first.
Here is an example of a Dockerfile creating a Linux image with an empty directory therein and advertising that directory to be used as a Bind mount or Volume in the resulting image:
FROM busybox
RUN mkdir /test # Create empty directory
VOLUME /test # Advertise directory for future Bind mount or Volume
During the runtime of each WSL 2 instance, the Linux file system is automatically mapped to a hidden file share in the Windows host OS, named after the WSL 2 instance name: \\WSL$\<WSL_INSTANCE_NAME>
. Vice versa, the Windows file system is mapped into a WSL 2 Linux file system at /mnt
. (The docker-desktop
mount point differs here, for this instance the Windows host OS file system is mounted to /mnt/host
.)
Bind mount handling
A Linux container’s read/write access to an advertised file or directory can be redirected to two different destinations by using Bind mounts:
- a file or directory in the Windows host file system
- a file or directory in another WSL 2 Linux instance
Bind-mounting to Windows host file system
Redirecting Linux container volumes to the Windows host file system comes with a severe performance penalty. When a Linux container is started using a Windows host Bind mount, Docker for Windows will chime a Windows notification informing the user about the performance ramifications that come with using Windows host file system Bind mounts.
Here is an example of Bind-mounting a Linux container to a directory in the Windows host file system:
docker run -it --rm type=bind,src=C:\Temp,dst=/test volumetest
docker run -it --rm -v C:\Temp:/test volumetest
Remoting data this way from the Windows host file system to the Linux container instance comes with a performance penalty. We therefore recommend storing data that is supposed to be Bind-mounted into WSL 2 Linux instances first and then mount from these instances.
Bind-mounting to another WSL 2 Linux instance
Before using Bind-mounting to files or directories of a WSL 2 Linux instance, make sure Docker integration is enabled in the corresponding WSL 2 instance.
To gain the best experience and benefit from maximum performance when Bind-mounting another WSL 2 Linux distro, create the Linux container from within the other WSL 2 Linux distro.
$ docker run -it --rm type=bind,src=~/test,dst=/test volumetest
$ docker run -it --rm -v ~/test:/test volumetest
Although files or directories in another WSL 2 Linux instance can be Bind-mounted by creating the Docker container in the Windows host OS using the hidden shared directory that is being created for each WSL 2 instance, this is strongly discouraged, as the file data gets remoted from Linux to Windows and back again to Linux in the other instance.
Only for the sake of completeness, the following example creates and uses /test
in a “Ubuntu” WSL 2 distro (the most common WSL install) to store the Docker container’s data using a hidden file share:
docker run -it --rm type=bind,src=\\wsl$\ubuntu\etc,dst=/test volumetest
docker run -it --rm -v \\wsl$\ubuntu\etc:/test volumetest
Using Volumes
Using Linux containers in Docker for Windows, local container Volumes will be stored in the docker-desktop-data
WSL 2 container instance. The presence of that instance can be displayed using the wsl -l -v
command. It’s a “black box” WSL instance which cannot be launched. It’s simply used by the Docker Desktop back-end for storage of images and volumes (and likely other Docker artifacts).
NB: There is a way to inspect the contents of that WSL instance. See this answer on SuperUser.com for details.
For further details on Volumes in Docker for Windows to above section “Volume: Store runtime data in a special Managed Volume storage area”.
storage, persistence, data persistence, volumes, bind-mounts, windows