Wordpress: wp-content is owned by root on creation

Created on 24 Sep 2019  路  22Comments  路  Source: docker-library/wordpress

It seems we should be able to mount themes and plugins and uploads separately, but for some reason it appears that when I remove and re-create wordpress using docker-compose, wp-content is always owned by root.

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    container_name: wordpress
    restart: always
    volumes:
      - ./wordpress/themes:/var/www/html/wp-content/themes:rw
      - ./wordpress/plugins:/var/www/html/wp-content/plugins:rw
      - ./wordpress/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: xxx
      WORDPRESS_DB_PASSWORD: xxx 
      WORDPRESS_DB_NAME: xxx
      VIRTUAL_HOST: xxx
-rw-r--r--  1 www-data www-data 4.7K Nov 30  2017 wp-trackback.php
-rw-r--r--  1 www-data www-data 8.3K Nov 30  2017 wp-mail.php
-rw-r--r--  1 www-data www-data 3.3K Nov 30  2017 wp-load.php
-rw-r--r--  1 www-data www-data  369 Nov 30  2017 wp-blog-header.php
-rw-r--r--  1 www-data www-data  420 Nov 30  2017 index.php
-rw-r--r--  1 www-data www-data 3.0K Aug 17  2018 xmlrpc.php
-rw-r--r--  1 www-data www-data  20K Jan  1  2019 license.txt
-rw-r--r--  1 www-data www-data 3.8K Jan  9  2019 wp-cron.php
-rw-r--r--  1 www-data www-data 6.8K Jan 12  2019 wp-activate.php
-rw-r--r--  1 www-data www-data 2.5K Jan 16  2019 wp-links-opml.php
-rw-r--r--  1 www-data www-data  31K Jan 16  2019 wp-signup.php
-rw-r--r--  1 www-data www-data 2.3K Jan 21  2019 wp-comments-post.php
-rw-r--r--  1 www-data www-data  19K Mar 28 19:04 wp-settings.php
-rw-r--r--  1 www-data www-data 7.3K Apr  8 22:59 readme.html
-rw-r--r--  1 www-data www-data  39K Jun 10 13:34 wp-login.php
drwxr-xr-x  9 www-data www-data 4.0K Sep  5 01:08 wp-admin
drwxr-xr-x 20 www-data www-data  12K Sep  5 01:08 wp-includes
drwxr-xr-x  1 root     root     4.0K Sep 12 09:32 ..
drwxr-xr-x  4 root     root     4.0K Sep 24 02:17 wp-content
-rw-r--r--  1 www-data www-data  234 Sep 24 02:17 .htaccess
-rw-r--r--  1 www-data www-data 2.8K Sep 24 02:17 wp-config-sample.php
-rw-r--r--  1 www-data www-data 3.2K Sep 24 02:17 wp-config.php
drwxrwxrwx  5 www-data www-data 4.0K Sep 24 02:17 .
question

Most helpful comment

I support it.. I do it a bit differently - I copy in advance wp-content folder and my chown commands gets totally ignored:

Here is my dockerfile:

FROM wordpress:5-php7.1-apache
COPY files/ /var/www/html
RUN chown -R www-data:www-data /var/www/html

I still get everything as root:root
Running under ECS fargate, there is no option to mount anything.

All 22 comments

If you mount the parent directory as well it should have appropriate permissions

https://github.com/docker-library/wordpress/issues/426#issuecomment-527581895

So when just the subdirectories of wp-content/ are mounted it makes wp-content owned by root and docker-entrypoint.sh doesn't do a wordpress install as it detects present files and assumed the wp-content is fully populated

So is that a workaround? It seems like a common need and something that could be checked for in entrypoint.sh

The parent wp-content directory doesn't exist in the image before the entrypoint is executed. So when subdirectories are mounted under it, then it needs to be created and is done so as root

makes sense. i just assumed there could be a one line script to just check for that folder afterwards and chown accordingly... but I'm admittedly somewhat green here.

I support it.. I do it a bit differently - I copy in advance wp-content folder and my chown commands gets totally ignored:

Here is my dockerfile:

FROM wordpress:5-php7.1-apache
COPY files/ /var/www/html
RUN chown -R www-data:www-data /var/www/html

I still get everything as root:root
Running under ECS fargate, there is no option to mount anything.

I changed my docker-compose.yml to mount wp-content instead of the child directories but the problem persists.

Does anyone have a working example of a docker-compose.yml that actually allows mounting a shared theme/plugin without causing permission issues in the docker container?

I've been running in circles for a long time now trying to switch to Docker for automated e2e testing and I just can't get past these permissions issues.

I now have it so it's mounting directories as 1000:1000 (the uid/gid of my host user) but the filesystem still doesn't work right as a result.

Edit: word

I'm my case, because I'm only binding volumes in docker-compose, I've solved the issue by chowning before running Apache, like this:

command: bash -c "/bin/chown -R www-data:www-data /var/www/html && apache2-foreground"

EDIT: For some reason, the above doesn't work, as it fails to copy the contents from /usr/src/wordpress on container first run.

I made some changes to fix this issue in #474

Any solution to it?

this is still a problem for me

it works if i add to my docker-compose.yml
command: /bin/sh -c "chown www-data:www-data wp-content wp-content/plugins wp-content/themes && exec apache2-foreground"

It would help to have a minimal example to reproduce the issue if you want us to look into it. :wink:

@tianon this is the docker-compose.yml that worked for me:

version: '3.1'
services:
  homeconvey:
    image: wordpress
    restart: always    
    environment:
      WORDPRESS_DB_HOST: $WORDPRESS_DB_HOST
      WORDPRESS_DB_USER: $WORDPRESS_DB_USER
      WORDPRESS_DB_PASSWORD: $WORDPRESS_DB_PASSWORD
      WORDPRESS_DB_NAME: $WORDPRESS_DB_NAME
      WORDPRESS_TABLE_PREFIX: $WORDPRESS_TABLE_PREFIX
    #   WORDPRESS_AUTH_KEY:
    #   WORDPRESS_SECURE_AUTH_KEY:
    #   WORDPRESS_LOGGED_IN_KEY:
    #   WORDPRESS_NONCE_KEY:
    #   WORDPRESS_AUTH_SALT:
    #   WORDPRESS_SECURE_AUTH_SALT:
    #   WORDPRESS_LOGGED_IN_SALT:
    #   WORDPRESS_NONCE_SALT:
    #   WORDPRESS_DEBUG:
    #   WORDPRESS_CONFIG_EXTRA:
    ports:
      - '8080:80'
    volumes:
      - $VOLUME_PATH:/var/www/html
      - /path/to/my/theme/:/var/www/html/wp-content/themes/homeconveytheme/:ro
      - /path/to/my/plugin/:/var/www/html/wp-content/plugins/homeconveyplugin/:ro
    command: /bin/sh -c "chown www-data:www-data wp-content wp-content/plugins wp-content/themes && exec apache2-foreground"
    networks:
      - mariadb-docker_backend
networks:
  mariadb-docker_backend:
    external: true

i am using docker to be able to see my plugin and theme read only during development.

without the chown command it seems everything gets owned by root so the installation cannot install any plugins or updates or anything. seems to happen when linking the bottom 2 volumes

everything gets owned by root

Are you using a new enough image? https://github.com/docker-library/wordpress/pull/503 was added for this exact use case (and built with https://github.com/docker-library/official-images/pull/8200). You'll likely need to have an empty $VOLUME_PATH to be able it to use it (and nocopy: false on the volume options if it is a mounted folder and not a docker volume)

When creating bind mounts, using the long syntax requires the referenced folder to be created beforehand. Using the short syntax creates the folder on the fly if it doesn鈥檛 exist.

- https://docs.docker.com/compose/compose-file/#long-syntax-3

So, if you use a host path in a docker-compose.yml for volumes and it does not already exist on the host, then dockerd will create it for you as root owned. If you create the directory first docker will not change the permissions or ownership and everything will just work (assuming the www-data user has access to the mounted directory & files).

  wordpress:
    image: wordpress
    volumes:
      - './theme:/var/www/html/wp-content/themes/homeconveytheme/:ro'
      - './plugin:/var/www/html/wp-content/plugins/homeconveyplugin/:ro'

Not pre-creating the directories:

$ docker pull wordpress
Using default tag: latest
latest: Pulling from library/wordpress
...
Digest: sha256:93ee786387237f25705610977d5f506c87ea99b1f207aa2441a027b2b5f8a7a2
Status: Downloaded newer image for wordpress:latest
docker.io/library/wordpress:latest
$ docker-compose -f docker-compose.yml run wordpress bash
root@281136d991cb:/var/www/html# ls -la
total 12
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 .
drwxr-xr-x 1 root     root     4096 Aug  5 04:17 ..
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:00 wp-content
root@281136d991cb:/var/www/html# ls -la wp-content/
total 16
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:00 .
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 ..
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 plugins
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 themes
root@281136d991cb:/var/www/html# ls -la wp-content/*
wp-content/plugins:
total 12
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 .
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:00 ..
drwxr-sr-x 2 root     root     4096 Sep  2 16:59 homeconveyplugin

wp-content/themes:
total 12
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:00 .
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:00 ..
drwxr-sr-x 2 root     root     4096 Sep  2 16:59 homeconveytheme

Pre-creating the directories:

$ sudo rmdir plugin/ theme/
$ mkdir plugin/ theme/
$ ls -ln
total 12
-rw-rw-r-- 1 1000 1000  960 Sep  2 09:59 docker-compose.yml
drwxr-sr-x 2 1000 1000 4096 Sep  2 10:06 plugin
drwxr-sr-x 2 1000 1000 4096 Sep  2 10:06 theme
$ docker-compose -f docker-compose.yml run wordpress bash
root@b1f7239f8720:/var/www/html# ls -la wp-content/*
wp-content/plugins:
total 12
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:07 .
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:07 ..
drwxr-sr-x 2     1000     1000 4096 Sep  2 17:06 homeconveyplugin

wp-content/themes:
total 12
drwxrwxrwx 3 www-data www-data 4096 Sep  2 17:07 .
drwxrwxrwx 4 www-data www-data 4096 Sep  2 17:07 ..
drwxr-sr-x 2     1000     1000 4096 Sep  2 17:06 homeconveytheme
root@b1f7239f8720:/var/www/html# 

@yosifkit was your $VOLUME_PATH folder completely empty before running docker-compose build? the wp-content directory and sub folders get created as root on my end. i am pretty sure i am using the latest because i only just started using it in august

I was using an anonymous docker volume and not a host path for /var/www/html. If you are using a host path and it is empty before starting, then you need to use the long syntax along with volume: { nocopy: false } (https://docs.docker.com/compose/compose-file/#long-syntax-3).

  volumes:
    - type: bind
      source: $VOLUME_PATH
      target: /var/www/html
      volume:
        nocopy: true

EDIT: this doesn't work :cry: (https://github.com/docker-library/wordpress/issues/436#issuecomment-687290394)

@yosifkit in order to use long form i had to change to version 3.3 at the top of the docker compose file. i cleared the directory and ran it. there is no change as the wp-content directory is still owned by root. i am not even sure if the volume->nocopy applies to a type of bind as it isn't a volume? i might consider switching to a volume as i don't really need to bind to filesystem. or otherwise i can just stick with my solution. thanks

i can confirm that if i use a volume it works fine

Yeah, apparently Docker thought it wouldn't be useful to allow nocopy to apply to a bind mount (I assumed that it would apply).

So if you need to mount both the base wordpress dir, and custom themes/plugins directories, then the empty directories need to be created within the wordpress directory with the correct permissions. Alternatively, you could just use one mount and put the custom themes directly in your wordpress folder (assuming you didn't need the ro on your themes/plugins).

i can confirm that if i use a volume it works fine

Or just use a named docker volume.

I originally published this solution in Stackoverflow, but because they're referring to a similar issue I'll publish it also here in case it helps someone.

Problem

The parent docker-entrypoint.sh is ran after your Dockerfile and entrypoint.sh, overwritting the permissions you set.

Solution

Custom entrypoint executing a delayed command that sets the ownership after docker-entrypoint.sh has ran.

Your Dockerfile

FROM wordpress:php7.4-apache

[...]

ENTRYPOINT ["/entrypoint.sh"]

Your entrypoint.sh


# In 10 seconds set the ownership

$(sleep 10 && chown -R www-data:www-data /var/www/html/) & 

# Run the parent's ENTRYPOINT and CMD as defined in its Dockerfile
#     (https://github.com/gsusI/wordpress/blob/master/php7.4/apache/Dockerfile)

docker-entrypoint.sh apache2-foreground # run parent entrypoint.

Note: Invoking the parent's ENTRYPOINT and CMD manually means that if the parent Dockerfile alters its last two lines this solution might not work properly.

Therefore is recommendable that wheneber updating the your FROM, you check the parent's Dockerfile to verify the last two lines are still:

ENTRYPOINT ["docker-entrypoint.sh"]

CMD ["apache2-foreground"]
Was this page helpful?
0 / 5 - 0 ratings