1. Overview
Docker has multiple options to persist and share data for a running container. However, we may need more than one file storage for a running container, for example, to create backups or grant different access. Or for the same container, we may need to add named volumes and bind them to specific paths.
In this tutorial, we'll see how to mount multiple volumes on a container. We'll see some examples both with the command line and with Docker Compose.
2. Multiple Mounts on a Docker Container
Docker uses Storage to persist data, so we are not losing our information if a container restarts. Furthermore, data persistence will be relevant if we want to share in a clustered environment.
We'll create some examples using Volumes and Bind mounts to highlight the most common development use cases.
2.1. Using Multiple Volumes
First, let's create two different named volumes:
docker volume create --name first-volume-data && docker volume create --name second-volume-data
Suppose we want to mount two different volumes for our web application, but one of those paths must be read-only.
If we are using the command line, we can use the -v option:
docker run -d -p 8080:8080 -v first-volume-data:/container-path-1 -v second-volume-data:/container-path-2:ro --name web-app web-app:latest
We can also use anonymous volumes, for example, by including -v container-path. Docker will create it for us, but it gets removed once we delete the container.
Let's inspect our container to check if our mounts are correct:
docker inspect 0050cda73c6f
We can see relevant information like sources and destinations, types, and also a read-only status for the second volume:
"Mounts": [
{
"Type": "volume",
"Name": "first-volume-data",
"Source": "/var/lib/docker/volumes/first-volume-data/_data",
"Destination": "/container-path-1",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "second-volume-data",
"Source": "/var/lib/docker/volumes/second-volume-data/_data",
"Destination": "/container-path-2",
"Driver": "local",
"Mode": "z",
"RW": false,
"Propagation": ""
}
]
Similarly, Docker recommends we use the –mount option:
docker run -d \
--name web-app \
-p 8080:8080 \
--mount source=first-volume-data,target=/container-path-1 \
--mount source=second-volume-data,target=/container-path-2,readonly \
web-app:latest
The result is the same as the -v option. However, besides better clarity, –mount is the only way we can use volumes with Services when we are in Swarm mode.
So, if we want to create a service for our web app which has multiple mounts, we need to use the –mount option:
docker service create --name web-app-service \
--replicas 3 \
--publish published=8080,target=80 \
--mount source=first-volume-data,target=/container-path-1 \
--mount source=second-volume-data,target=/container-path-2,readonly \
web-app
Similarly, we can inspect our service:
docker service inspect web-app-service
Likewise, we'll get some info about the container inside our service specifications:
"Mounts": [
{
"Type": "volume",
"Source": "first-volume-data",
"Target": "/container-path-1"
},
{
"Type": "volume",
"Source": "second-volume-data",
"Target": "/container-path-2",
"ReadOnly": true
}
]
2.2. Using Volumes and Bind Mounts
We may also want to use a volume alongside a mount to a specific folder or file in our host.
Suppose we have a MySQL database image, and we need to run an initial script to create a schema or populate it with some data:
docker run -d \
--name db \
-p 3306:3306 \
--mount source=first-volume-data,target=/var/lib/mysql \
--mount type=bind,source=/init.sql,target=/docker-entrypoint-initdb.d/init.sql \
mysql:latest
If we inspect our container, we can now see two different mount types:
"Mounts": [
{
"Type": "volume",
"Name": "first-volume-data",
"Source": "/var/lib/docker/volumes/first-volume-data/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
},
{
"Type": "bind",
"Source": "/init.sql",
"Destination": "/docker-entrypoint-initdb.d/init.sql",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
2.3. Using Multiple Bind Mounts
Similarly, we can use multiple bind mounts, for example, when we use Localstack, a local AWS emulator.
Suppose we want to start a local S3 service:
docker run --name localstack -d \
-p 4563-4599:4563-4599 -p 8055:8080 \
-e SERVICES=s3 -e DEBUG=1 -e DATA_DIR=/tmp/localstack/data \
-v /.localstack:/var/lib/localstack -v /var/run/docker.sock:/var/run/docker.sock \
localstack/localstack
While inspecting the container, we see multiple binds in our host configuration:
"Binds": [
"/.localstack:/var/lib/localstack",
"/var/run/docker.sock:/var/run/docker.sock"
]
3. Docker Compose
Let's see a more compact way for multiple mounts with Docker Compose.
3.1. Using Multiple Volumes
First, let's start with two volumes, as shown in our YAML template:
services:
my_app:
image: web-app:latest
container_name: web-app
ports:
- "8080:8080"
volumes:
- first-volume-data:/container-path-1
- second-volume-data:/container-path-2:ro
volumes:
first-volume-data:
driver: local
second-volume-data:
driver: local
Once we define the volumes we created earlier, we can add the mount in the service in the form of named-volume:container-path.
The Long syntax is also available for Docker Compose, for example:
volumes:
- type: volume
source: volume-data
target: /container-path
3.2. Using Volumes and Bind Mounts
Again, here's an example of using a MySQL service:
services:
mysql-db:
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_ROOT_HOST=localhost
ports:
- '3306:3306'
volumes:
- db:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db:
driver: local
3.3. Using Multiple Bind Mounts
Finally, let's convert our previous Localstack example to Docker Compose:
services:
localstack:
privileged: true
image: localstack/localstack:latest
container_name: localstack
ports:
- '4563-4599:4563-4599'
- '8055:8080'
environment:
- SERVICES=s3
- DEBUG=1
- DATA_DIR=/tmp/localstack/data
volumes:
- './.localstack:/var/lib/localstack'
- '/var/run/docker.sock:/var/run/docker.sock'
4. Conclusion
In this article, we've seen how to create multiple mounts using Docker.
We've seen some combinations of bind mounts and named volumes. We have also seen use cases using the Docker command line and Docker Compose.
As always, we can find working code examples over on GitHub.
res – REST with Spring (eBook) (everywhere)