Turns out that named and anonymous Docker volumes (not host mounted volumes) are always owned by root
even if those files and directories already exist inside the container with different permissions. This is probably due to the Docker engine creating those virtual directories after the containers are built.
For example, the WordPress Docker image sets the owner and group of all WordPress core files to www-data
when copying them to the public directory for the webserver:
sourceTarArgs=(
--create
--file -
--directory /usr/src/wordpress
--owner "$user" --group "$group"
)
targetTarArgs=(
--extract
--file -
)
# ...
tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}"
echo >&2 "Complete! WordPress has been successfully copied to $PWD"
and uses tar
to copy the files to avoid overriding any existing files and directories in the destination directory which are most likely either named or virtual host volumes.
Virtual and Host Volumes
For example, a project directory is mapped to /var/www/html/wp-content/plugins/plugin-name
inside a container which is also a subdirectory of a named volume wp_data:/var/www/html
:
version: '3'
services:
wordpress:
image: wordpress:php7.3-apache
depends_on:
- mysql
ports:
- "80:80"
volumes:
- wp_data:/var/www/html
- .:/var/www/html/wp-content/plugins/plugin-name
restart: always
environment:
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: password
# ...
The goal of the named volume wp_data
is to persist the /var/www/html
directory between container reboots.
Unfortunately, all files and directories created as volumes by the Docker engine (that don’t map to existing files and directories on the Docker host) are owned by root:root
.
So the /var/www/html/wp-content
directory inside the container ends up with the following permissions:
$ docker-compose exec wordpress ls -lah wp-content
drwxr-xr-x 4 root root 4.0K Jul 12 08:40 .
drwxrwxrwx 6 www-data www-data 4.0K Jul 12 08:40 ..
-rw-r--r-- 1 www-data www-data 28 Jan 8 2012 index.php
drwxr-xr-x 5 root root 4.0K Jul 12 08:40 plugins
drwxr-xr-x 5 www-data www-data 4.0K Jun 18 17:51 themes
Note that wp-content
and the mapped wp-content/plugins
directories are owned by root:root
while the parent directory ..
and wp-content/themes
directories created during the container build have the correct owner www-data
as set during the container build.
However, the permissions of the mapped plugin-name
directory do match those on the Docker host 1000:1000
while the directory tree up until that directory is owned by root
:
$ docker-compose exec wordpress ls -lah wp-content/plugins
drwxr-xr-x 5 root root 4.0K Jul 12 09:58 .
drwxr-xr-x 4 root root 4.0K Jul 12 09:58 ..
-rw-r--r-- 1 www-data www-data 28 Jun 5 2014 index.php
drwxr-xr-x 1 1000 1000 1.2K Jul 12 07:27 plugin-name
This prevents the application running in the container from writing to these directories since they’re owned by root
.
Solution: Use Host Volumes
Instead of using named volumes wp_data:/var/www/html
to persist the data, map it to a local directory ./local/public:/var/www/html
instead:
version: '3'
services:
wordpress:
image: wordpress:php7.3-apache
depends_on:
- mysql
ports:
- "80:80"
volumes:
- ./local/public:/var/www/html
- .:/var/www/html/wp-content/plugins/plugin-name
restart: always
environment:
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: password
# ...
which will make it inherit the owner of the directory on the Docker host:
$ docker-compose exec wordpress ls -lah wp-content
total 4.0K
drwxr-xr-x 1 1000 1000 224 Jul 12 08:57 .
drwxr-xr-x 1 1000 1000 768 Jul 12 09:04 ..
-rw-r--r-- 1 1000 1000 28 Jan 8 2012 index.php
drwxr-xr-x 1 1000 1000 224 Jul 12 08:55 plugins
drwxr-xr-x 1 1000 1000 224 Jul 12 08:57 themes
drwxr-xr-x 1 1000 1000 64 Jul 12 08:57 upgrade
drwxr-xr-x 1 1000 1000 96 Jul 12 08:57 uploads
Now all files are owned by the user ID 1000
with full permissions.
Same problem in postgres container. Docker compose should allow to set permission when mounting the volumes.