---
title: Docker Volume Permissions
date: 2019-07-12T10:30:49+00:00
modified: 2019-11-27T16:09:02+00:00
image:: https://kaspars.net/wp-content/uploads/2019/07/docker-volume-permissions-root.png
permalink: https://kaspars.net/blog/docker-volume-permissions
post_type: post
author:
  name: Kaspars
  avatar: https://reverse.kaspars.net/gravatar/avatar/92bfcd3a8c3a21a033a6484d32c25a40b113ec6891f674336081513d5c98ef76?s=96&d=mm&r=g
category:
  - Development
  - WordPress
---

# Docker Volume Permissions

Turns out that [named and anonymous Docker volumes](https://success.docker.com/article/different-types-of-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](https://github.com/docker-library/wordpress/blob/fc7f07cb8bd12b4f4a8d30710348af0445ce46af/docker-entrypoint.sh#L62):

```
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](https://success.docker.com/article/different-types-of-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.