Docker-compose lets you create and run multi-container Docker apps. Compose allows you to configure your app’s services with YAML files. You can then run all of the services you have created using one command. There are two options for declaring volumes in Docker. The declarative method (Docker client) or the imperative way (Docker Compose Yaml File or Docker Dockerfile). To share data among multiple containers, it is easier to use docker-compose.
This article will explain everything you need to know about docker volume.
Volume Using docker-compose
It is easy to create a volume with docker-compose. Start with something similar to a container and then mention the name you want to mount in it. You can use lazy syntax if you don’t include a name.
Compose files uses the volume property to perform a bind mount (mount a directory from your local machine). This is similar to –volume and -v. To perform a bind (mount a directory on your local machine), you will need a relative path. This is in contrast to -v which requires an absolute path.
version: "3.2"
services:
web:
image: nginx:latest
ports:
- 8080:80
volumes:
- ./target:/usr/share/nginx/html
The containers and hosts in the above configuration use volumes in the services definition (web) to mount ./target from the host to /usr/share/nginx/html of the container. Volumes must be declared at the exact same level as services when using docker-compose. You can then refer to them as their names.
version: "3.2"
services:
web:
image: nginx:latest
ports:
- 8080:80
volumes:
- html_files:/usr/share/nginx/html
web1:
image: nginx:latest
ports:
- 8081:80
volumes:
- html_files:/usr/share/nginx/html
volumes:
html_files:
This example shows how you created a volume called html_files, and then used it in both the web1 and web2 services. Multiple containers can mount the exact same volume.
Docker-compose up will generate a volume called If it does not already exist, _html_files. Run docker volume ls for a list of the volumes created. Start with the project name.
External Volume
We can also create a volume outside of Docker Compose and then reference it inside the ‘docker-compose.yaml’ file, as shown in an example below.
version: "3.3"
services:
frontend:
image: node:lts
volumes:
- myvolume:/home/node/app
volumes:
myvolume:
external: true
The volume name “myvolume” is used in the docker-compose above file. A flag called ‘external’ has been specified. It is set to true and indicates that the volume was created outside of docker-compose.
Shared Volume
Syntax: –volumes-from container_name
You can create a new container by using volumes from another volume. Similar to –volume or -v, but without the need to specify a volume or mount paths.
$ docker run -it –name [my_new_container] –volumes-from [another_container] [image:tag] [command]
Note: –volumes from make sense if you are only using Docker. We can use top-level volumes for Docker-compose and make them available to multiple services. Example: Sharing web_data to app2 and app1:
volumes:
web_data:
external: true
services:
app:
image: nginx:alpine
ports:
- 80:80
volumes:
- web_data:/usr/share/nginx/html:ro
app2:
image: nginx:alpine
ports:
- 8080:80
volumes:
- web_data:/usr/share/nginx/html:ro
Docker Compose Volume Permissions
Let’s suppose you want to run WordPress in a docker container. The docker-compose.yaml is:
version: "2"
services:
my-wpdb:
image: mariadb
ports:
- "8081:3306"
environment:
MYSQL_ROOT_PASSWORD: test@1234
my-wp:
image: wordpress
volumes:
- ./:/var/www/html
ports:
- "8080:80"
links:
- my-wpdb:mysql
environment:
WORDPRESS_DB_PASSWORD: demo@1234
According to the docker-compose, the user option sets the user id (and group id) of the process running in the container. If you set this to 1000:1000, your webserver is not able to bind to port 80 anymore. Binding to a port below 1024 requires root permissions. This means you should remove the added user: 1000:1000 statement again.
To provide the permission to the shared volume, you need to change the ownership of the directory. Run chown 1000:1000 /path/to/volume. This can be executed inside the container or directly on the host system. The change is persistent and effective immediately (no container restarted required).
In general, I think the volume should be in a sub-directory, e.g.
volumes:
– ./public:/var/www/html
Verify that the correct user has ./public. Docker will create the directory for you if you start the container but it does not exist. If this happens, root is the owner of the directory and you will need to manually change ownership as described above.
Alternatively, you could run the webserver with an unprivileged user (user : 1000:1000), and let the server listen to port 8080. You can then change routing to:
ports:
– “8080:8080”
You can grant full permission to a volume by using user: root in the docker compose file.
To give read permission to a volume, you can use ro.
volumes:
– ./:/var/www:ro #read only
Final Thoughts
It can be extremely helpful to organize your apps so that each app is distinct from the data. Volumes are an efficient way to achieve this. You can use them as disposable environments even in production, provided they are secure and backed up.
Docker Compose lets you configure volumes using a very short syntax string. It is up to you which syntax variation you use that determines whether you end with a volume, or a binding mount.