[{"id":"160dfa1d-c1ed-4b14-ba2e-5b9e104b5dcb","thread_id":"d1257e76-261a-4c4f-b1dc-5b43a05284ce","created_at":"2026-05-26T13:00:36Z","branch_list":["00000000-0000-4000-0000-000000000000"],"state":"done","prompt":"investigate this issue https://git.coopcloud.tech/coop-cloud/wordpress/issues/56","references_html":"<ol data-ref-list>\n \n <li>\n <a href=\"https://github.com/docker-library/wordpress/issues/436\" >wp-content is owned by root on creation · Issue #436...</a>\n <span class=\"__domain-name\">github.com</span>\n <div class=\"trigger\" tabindex=\"0\">\n <span class=\"contribution\" style=\"background: linear-gradient(to right, var(--primary-70) 72%, transparent 72%)\">72%</span>\n <div class=\"tooltip top end tooltip_base tw-auto\">\n This percentage indicates how much a source has contributed to the answer.\n </div>\n </div>\n </li>\n \n <li>\n <a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56\" >#56 - Bug: default Wordpress deploys uploads folder owned by root - wordpress - Co-op Cloud Code</a>\n <span class=\"__domain-name\">git.coopcloud.tech</span>\n <div class=\"trigger\" tabindex=\"0\">\n <span class=\"contribution\" style=\"background: linear-gradient(to right, var(--primary-70) 28%, transparent 28%)\">28%</span>\n <div class=\"tooltip top end tooltip_base tw-auto\">\n This percentage indicates how much a source has contributed to the answer.\n </div>\n </div>\n </li>\n \n <li>\n <a href=\"https://kaspars.net/blog/docker-volume-permissions\" >Docker Volume Permissions (Development, WordPress) — Kaspars Dambis</a>\n <span class=\"__domain-name\">kaspars.net</span>\n <div class=\"trigger\" tabindex=\"0\">\n <span class=\"contribution\" style=\"background: linear-gradient(to right, var(--primary-70) < 1%, transparent < 1%)\">< 1%</span>\n <div class=\"tooltip top end tooltip_base tw-auto\">\n This percentage indicates how much a source has contributed to the answer.\n </div>\n </div>\n </li>\n \n</ol>\n","references_md":"[^1]: [wp-content is owned by root on creation · Issue #436...](https://github.com/docker-library/wordpress/issues/436) (72%)\n[^2]: [#56 - Bug: default Wordpress deploys uploads folder owned by root - wordpress - Co-op Cloud Code](https://git.coopcloud.tech/coop-cloud/wordpress/issues/56) (28%)\n[^3]: [Docker Volume Permissions (Development, WordPress) — Kaspars Dambis](https://kaspars.net/blog/docker-volume-permissions) (< 1%)\n","reply":"<details><summary>Gathered details on Reading GitHub issue from<p></p>\n<ul>\n<li><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56\" target=\"_blank\">#56 - Bug: default Wordpress deploys uploads folder owned by root - wordpress - Co-op Cloud Code</a></li></ul></summary>\n\n<h3>Query</h3>\n<p>What is this issue about? Provide the full details including the title, description, comments, and any code or configuration mentioned.</p>\n<h3>Response</h3>\n<h1>Bug: default Wordpress deploys uploads folder owned by root#56</h1>\n<p>Context: # Bug: default Wordpress deploys uploads folder owned by root#56</p>\n<hr>\n<p>We're finding that we're seeing this on new deploys:</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>root@144f17f3106e:/var/www/html/wp-content# ls -la\ntotal 36\ndrwxr-xr-x 8 www-data www-data 4096 Dec 1 06:11 .\ndrwxr-xr-x 5 www-data www-data 4096 May 26 12:53 ..\ndrwxrwxrwt 2 www-data www-data 4096 May 20 00:20 cache\n-rw-r--r-- 1 www-data www-data 28 Jan 8 2012 index.php\ndrwxr-xr-x 4 www-data www-data 4096 Nov 12 2025 plugins\ndrwxr-xr-x 5 www-data www-data 4096 Dec 1 06:11 themes\ndrwxr-xr-x 2 www-data www-data 4096 May 21 14:07 upgrade\ndrwxr-xr-x 3 www-data www-data 4096 May 21 14:04 upgrade-temp-backup\ndrwxr-xr-x 2 root root 4096 May 21 14:00 uploads\n</code></pre></div>\n<p><code>wp-content/uploads</code> folder isn't writable by <code>www-data</code> and is instead owned by <code>root</code> breaking uploads in the GUI.</p>\n<p>Context: We're finding that we're seeing this on new deploys:</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>root@144f17f3106e:/var/www/html/wp-content# ls -la\ntotal 36\ndrwxr-xr-x 8 www-data www-data 4096 Dec 1 06:11 .\ndrwxr-xr-x 5 www-data www-data 4096 May 26 12:53 ..\ndrwxrwxrwt 2 www-data www-data 4096 May 20 00:20 cache\n-rw-r--r-- 1 www-data www-data 28 Jan 8 2012 index.php\ndrwxr-xr-x 4 www-data www-data 4096 Nov 12 2025 plugins\ndrwxr-xr-x 5 www-data www-data 4096 Dec 1 06:11 themes\ndrwxr-xr-x 2 www-data www-data 4096 May 21 14:07 upgrade\ndrwxr-xr-x 3 www-data www-data 4096 May 21 14:04 upgrade-temp-backup\ndrwxr-xr-x 2 root root 4096 May 21 14:00 uploads\n</code></pre></div>\n<p><code>wp-content/uploads</code> folder isn't writable by <code>www-data</code> and is instead owned by <code>root</code> breaking uploads in the GUI.</p>\n<hr>\n<p>Fix is to go in the containers</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>abra app run foo.bar app bash\n</code></pre></div>\n<p>and run</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>chown -R www-data:www-data uploads/\n</code></pre></div>\n<p>Context: Fix is to go in the containers</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>abra app run foo.bar app bash\n</code></pre></div>\n<p>and run</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>chown -R www-data:www-data uploads/\n</code></pre></div>\n<hr>\n<p>Any idea on the cause?</p>\n<p>Context: Any idea on the cause?</p>\n</details><details><summary>Gathered details on Reading full issue comments from<p></p>\n<ul>\n<li><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56\" target=\"_blank\">#56 - Bug: default Wordpress deploys uploads folder owned by root - wordpress - Co-op Cloud Code</a></li></ul></summary>\n\n<h3>Query</h3>\n<p>Read the full issue including all comments, especially any discussion about the root cause, the Dockerfile, entrypoint script, or volume mounts that might explain why the uploads folder is owned by root.</p>\n<h3>Response</h3>\n<p>We're finding that we're seeing this on new deploys:</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>root@144f17f3106e:/var/www/html/wp-content# ls -la\ntotal 36\ndrwxr-xr-x 8 www-data www-data 4096 Dec 1 06:11 .\ndrwxr-xr-x 5 www-data www-data 4096 May 26 12:53 ..\ndrwxrwxrwt 2 www-data www-data 4096 May 20 00:20 cache\n-rw-r--r-- 1 www-data www-data 28 Jan 8 2012 index.php\ndrwxr-xr-x 4 www-data www-data 4096 Nov 12 2025 plugins\ndrwxr-xr-x 5 www-data www-data 4096 Dec 1 06:11 themes\ndrwxr-xr-x 2 www-data www-data 4096 May 21 14:07 upgrade\ndrwxr-xr-x 3 www-data www-data 4096 May 21 14:04 upgrade-temp-backup\ndrwxr-xr-x 2 root root 4096 May 21 14:00 uploads\n</code></pre></div>\n<p><code>wp-content/uploads</code> folder isn't writable by <code>www-data</code> and is instead owned by <code>root</code> breaking uploads in the GUI.</p>\n<p>Context: The issue reports that on new WordPress deploys, the <code>wp-content/uploads</code> folder is owned by <code>root</code> instead of <code>www-data</code>, which prevents uploads in the GUI.</p>\n<hr>\n<p>Fix is to go in the containers</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>abra app run foo.bar app bash\n</code></pre></div>\n<p>and run</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>chown -R www-data:www-data uploads/\n</code></pre></div>\n<p>Context: The user suggests a workaround to fix the permissions:</p>\n<div class=\"codehilite\"><span class=\"filename\"></span><pre><span></span><code>abra app run foo.bar app bash\nchown -R www-data:www-data uploads/\n</code></pre></div>\n<hr>\n<p>Any idea on the cause?</p>\n<p>Context: The user specifically asks for the cause of this behavior. There is no further discussion in the provided text regarding the root cause, Dockerfile, entrypoint script, or volume mounts.</p>\n</details><details><summary>Searched from git.coopcloud.tech with Kagi<p></p>\n<ul>\n<li>coop-cloud wordpress docker-compose uploads volume root ownership</li>\n<li>wordpress docker uploads folder owned by root volume mount cause</li></ul></summary>\n\n<p>Sources (10):<br>\n<strong>coop-cloud/outline: Wiki and knowledge base for growing teams - outline ...</strong><br>\n<a href=\"https://git.coopcloud.tech/coop-cloud/outline\" target=\"_blank\">https://git.coopcloud.tech/coop-cloud/outline</a><br>\nMar 02, 2022 compose.aws.yml should still be deployed! compose.aws.yml should still be deployed! ... on the docker host, find mountpoint of newly created volume via docker volume ls and docker volume inspect volume name is smth like <domain>_storage-data volume name is smth like <domain>_storage-data take note which linux user owns <storage_mountpoint ... ... Set up Docker Swarm and abra. Deploy coop-cloud/traefik. abra app new ${REPO_NAME}. WARNING: Choose \"n\" when abra asks if you'd like to generate secrets. abra app config YOURAPPNAME - be sure to change $DOMAIN to something that resolves to your Docker swarm box.abra app undeploy <domain>. on the docker host, find mountpoint of newly created volume via docker volume ls and docker volume inspect.</p>\n<p><strong>coop-cloud/wordpress: Open source software... - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/coop-cloud/wordpress\" target=\"_blank\">https://git.coopcloud.tech/coop-cloud/wordpress</a><br>\nJul 17, 2021 add some more wordpress options for file uploads ( #44 ).abra app config <app-name> Configure the following envs: COMPOSE_FILE=\"$COMPOSE_FILE:compose.authentik.yml\" AUTHENTIK_DOMAIN=authentik.example.com AUTHENTIK_SECRET_NAME=authentik_example_com_wordpress_secret_v1 # the same as in authentik AUTHENTIK_ID_NAME=authentik_example_com_wordpress_id_v1 # the same as in authentik.</p>\n<p><strong>mariha/docs.coopcloud.tech: Documentation for the Co-op Cloud project ...</strong><br>\n<a href=\"https://git.coopcloud.tech/mariha/docs.coopcloud.tech\" target=\"_blank\">https://git.coopcloud.tech/mariha/docs.coopcloud.tech</a><br>\n coop-cloud/docs.coopcloud.tech Documentation for the Co-op Cloud project 295 Commits 1 Branch 0 Tags 1.5 MiB Makefile 52.1% Dockerfile 47.9% main Go to file HTTPS</p>\n<p><strong>wiki-cafe/backup-bot-two: Backup Bot II: This... - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/wiki-cafe/backup-bot-two\" target=\"_blank\">https://git.coopcloud.tech/wiki-cafe/backup-bot-two</a><br>\nMay 30, 2024 When using docker exec, you don't need to specify the volume mounts or the Restic repository location as command-line arguments because they are already defined in the Docker Compose file and are available within the running container. If you need to access the downloaded files, volumes, or secrets from the backup, you can use docker cp to copy them from the container to the host machine</p>\n<p><strong>rix/docs.coopcloud.tech: Documentation for the Co-op Cloud project ...</strong><br>\n<a href=\"https://git.coopcloud.tech/rix/docs.coopcloud.tech\" target=\"_blank\">https://git.coopcloud.tech/rix/docs.coopcloud.tech</a><br>\n forked from coop-cloud/docs.coopcloud.tech Code Issues Pull Requests Packages Projects Releases Wiki Activity Documentation for the Co-op Cloud project 567 Commits 6 Branches 0 Tags 2.6 MiB HTML 93.8% Dockerfile 3.1% Makefile 3.1% main Go to file HTTPS</p>\n<p><strong>p4u1/wordpress: Open source software you can... - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/p4u1/wordpress\" target=\"_blank\">https://git.coopcloud.tech/p4u1/wordpress</a><br>\nJun 24, 2024 forked from coop-cloud/wordpress. Watch.abra app config <app-name> Configure the following envs: COMPOSE_FILE=\"$COMPOSE_FILE:compose.authentik.yml\" AUTHENTIK_DOMAIN=authentik.example.com AUTHENTIK_SECRET_NAME=authentik_example_com_wordpress_secret_v1 # the same as in authentik AUTHENTIK_ID_NAME=authentik_example_com_wordpress_id_v1 # the same as in authentik.</p>\n<p><strong>coop-cloud/distribution: The Docker toolset to pack, ship, store, and ...</strong><br>\n<a href=\"https://git.coopcloud.tech/coop-cloud/distribution\" target=\"_blank\">https://git.coopcloud.tech/coop-cloud/distribution</a><br>\n coop-cloud / distribution 1 Code Issues Pull Requests Releases Wiki Activity 20 Commits 1 Branch 0 Tags cellarspoon d05129e52b quick pass on updating old cruft 2021-12-21 00:54:04 +01:00 .drone.yml Remove trigger, we make the tags [ci skip] 2021-06-04 00:14:18 +02:00 compose.yml quick pass on updating old cruft 2021-12-21 00:54:04 +01:00 config ...</p>\n<p><strong>coop-cloud/authentik: The authentication glue... - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/coop-cloud/authentik\" target=\"_blank\">https://git.coopcloud.tech/coop-cloud/authentik</a><br>\nApr 24, 2022 coop-cloud/authentik. Watch. 1.compose.outposts.yml. security: don't expose docker socket by default.</p>\n<p><strong>Explore - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/\" target=\"_blank\">https://git.coopcloud.tech/</a><br>\n An online office suite comprising viewers and editors for texts, spreadsheets and presentations, fully compatible with Office Open XML formats: .docx, .xlsx, .pptx and enabling collaborative editing in real time.</p>\n<p><strong>ammaratef45/backup-bot-two: Backup Bot II: This... - Co-op Cloud Code</strong><br>\n<a href=\"https://git.coopcloud.tech/ammaratef45/backup-bot-two\" target=\"_blank\">https://git.coopcloud.tech/ammaratef45/backup-bot-two</a><br>\nOct 19, 2024 Automatically take backups from all volumes of running Docker Swarm services and runs pre- and post commands. Category: Utilities. Status: 0, work-in-progress. Image: git.coopcloud.tech/coop-cloud/backup-bot-two, 4, upstream. Healthcheck: No.There are lots of Docker volume backup systems; all of them have one or both of these limitations: You need to define all the volumes to back up in the configuration system. Backups require services to be stopped to take consistent copies. Backupbot II tries to help, by.</p>\n<hr>\n<p>Sources (10):<br>\n<strong>wp-uploads and changing file permissions · Issue #162 - GitHub</strong><br>\n<a href=\"https://github.com/docker-library/wordpress/issues/162\" target=\"_blank\">https://github.com/docker-library/wordpress/issues/162</a><br>\nJul 18, 2016 I've installed the official Docker for mac now and have lost the ability to upload files. Now all the files are set to root, but apache is operating as 'www- ...</p>\n<p><strong>wp-content is owned by root on creation · Issue #436...</strong><br>\n<a href=\"https://github.com/docker-library/wordpress/issues/436\" target=\"_blank\">https://github.com/docker-library/wordpress/issues/436</a><br>\nSep 26, 2019 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: - d...wordpress: depends_on: - db image: wordpress:latest container_name: wordpress restart: always volumes ... 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 ... ... 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.</p>\n<p><strong>Upgrade of plugins doesn't work if they are on a mounted volume...</strong><br>\n<a href=\"https://github.com/docker-library/wordpress/issues/352\" target=\"_blank\">https://github.com/docker-library/wordpress/issues/352</a><br>\nDec 06, 2018 I´m using docker compose to create all my wordpress container. If I create my container with mount volumes for plugins, themes and languages I can´t update/upgrade all of this. Here are my configurations: wordpress: image: wordpress:latest volumesUpdating akismet with it being owned by root, undergone a chmod 777, and being mounted to /usr/src/wordpress which will copy and chown to $user:$group in the container.</p>\n<p><strong>Docker Compose mounts named volumes as 'root' exclusively...</strong><br>\n<a href=\"https://github.com/docker/compose/issues/3270\" target=\"_blank\">https://github.com/docker/compose/issues/3270</a><br>\nApr 10, 2016 It's about named volumes (so no \"data volume container\", no \"volumes-from\") and docker-compose.yml.It's about named volumes (so no \"data volume container\", no \"volumes-from\") and docker-compose.yml. The goal here is to use docker-compose to manage two services 'appserver' and 'server-postgresql'...</p>\n<p><strong>Understanding User File Ownership in Docker: How to Avoid Changing ...</strong><br>\n<a href=\"https://linuxvox.com/blog/understanding-user-file-ownership-in-docker-how-to-avoid-changing-permissions-of-linked-volumes/\" target=\"_blank\">https://linuxvox.com/blog/understanding-user-file-ownership-in-docker-how-to-avoid-changing-permissions-of-linked-volumes/</a><br>\n If you’ve worked with Docker, chances are you’ve encountered a frustrating scenario: after mounting a host directory as a volume (a \"bind mount\") and running your container, you check the host directory only to find files owned by `root` or an unfamiliar user. Worse, trying to edit these files on the host results in \"permission denied\" errors. This common headache stems from how Docker ...</p>\n<p><strong>When the volume is mounted, the directory on the host is owned by ...</strong><br>\n<a href=\"https://www.reddit.com/r/docker/comments/vic0g5/when_the_volume_is_mounted_the_directory_on_the/\" target=\"_blank\">https://www.reddit.com/r/docker/comments/vic0g5/when_the_volume_is_mounted_the_directory_on_the/</a><br>\nJun 23, 2022 That is because docker by default run as root user. If you want your container to run as another user you have to use an image that supports ... ...</p>\n<p><strong>Docker Volume Permissions (Development, WordPress) — Kaspars Dambis</strong><br>\n<a href=\"https://kaspars.net/blog/docker-volume-permissions\" target=\"_blank\">https://kaspars.net/blog/docker-volume-permissions</a><br>\nJul 13, 2019 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. ... 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.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.</p>\n<p><strong>The Complete Guide to Docker Mount Permission Issues: From Diagnosis to ...</strong><br>\n<a href=\"https://eastondev.com/blog/en/posts/dev/20251217-docker-mount-permissions-guide/\" target=\"_blank\">https://eastondev.com/blog/en/posts/dev/20251217-docker-mount-permissions-guide/</a><br>\n Can't delete files generated by containers? Permission Denied errors everywhere? Deep dive into the root causes of Docker permission issues with 5 solutions covering Linux/Mac/Windows differences and 5 real-world cases. Master 3 diagnostic commands and solve permission problems for good.</p>\n<p><strong>Volume Management | kawamataryo/quick-start-wordpress-docker | DeepWiki</strong><br>\n<a href=\"https://deepwiki.com/kawamataryo/quick-start-wordpress-docker/4.2-volume-management\" target=\"_blank\">https://deepwiki.com/kawamataryo/quick-start-wordpress-docker/4.2-volume-management</a><br>\n This document covers the volume mounting strategy used in the Docker environment, including shared storage between containers, data persistence considerations, and configuration file access patterns.</p>\n<p><strong>How to resolve 'permission denied' error when mounting volume in...</strong><br>\n<a href=\"https://labex.io/tutorials/docker-how-to-resolve-permission-denied-error-when-mounting-volume-in-docker-417724\" target=\"_blank\">https://labex.io/tutorials/docker-how-to-resolve-permission-denied-error-when-mounting-volume-in-docker-417724</a><br>\nNov 03, 2024 When Docker mounts a host directory, the permission checks are still applied based on the original file permissions and user IDs. Understanding the Problem. The permission denied error occurs because: The file on your host is owned by your user (labex). The file has permissions set to 700 (only the owner can access it). The Docker container runs as a different user ID (usually root, which is UID 0).</p>\n</details><details><summary>Gathering key details from<br>\n</summary><br>\n You've reached the tool use limit and are now in response-only mode. You can no longer call tools. Focus on synthesizing the information you've already gathered to wrapping up a clear, well-structured final response.<p></p>\n</details><h2>Investigation: Co-op Cloud WordPress Issue #56 — <code>uploads</code> folder owned by <code>root</code></h2>\n<h3>The Problem</h3>\n<p>On new Co-op Cloud WordPress deploys, the <code>wp-content/uploads</code> directory is owned by <code>root:root</code> instead of <code>www-data:www-data</code>, which breaks file uploads in the WordPress GUI <sup class=\"trigger\"><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56#:~:text=coop-cloud/wordpress%208%200,Continue%3F%20No%20Yes\">2</a><span class=\"tooltip top center citation\"><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56#:~:text=coop-cloud/wordpress%208%200,Continue%3F%20No%20Yes\"><span class=\"source\"><img alt=\"\" src=\"https://p.kagi.com/proxy/favicons?c=lQ30-cxV_E2Vdyqb6hrbuYgId7qu7hecjO7o1T7aqQDFc7XpcLttL1Ryala-GNHJhUDE9IwDGX7Fz2IvgH_T8AvtTrnsMncaA3Iv4Y8VcoQ%3D\"> git.coopcloud.tech</span><b>2. #56 - Bug: default Wordpress deploys uploads folder owned by root - wordpress - Co-op Cloud Code</b></a><span class=\"passage\">coop-cloud/wordpress 8 0 Fork 2 You've already forked wordpress # Bug: default Wordpress deploys uploads folder owned by root#56 Open opened 2026-05-26 12:58:32 +00:00 by kawaiipunk · 0 comments kawaiipunk commented 2026-05-26 12:58:32 +00:00 Owner We're finding that we're seeing this on new deploys: ``` root@144f17f3106e:/var/www/html/wp-content# ls -la total 36 drwxr-xr-x 8 www-data www-data 4096 Dec 1 06:11 . drwxr-xr-x 5 www-data www-data 4096 May 26 12:53 .. drwxrwxrwt 2 www-data www- ...</span></span></sup>.</p>\n<div class=\"codehilite\"><span class=\"filename\">Plaintext</span><pre><span></span><code>drwxr-xr-x 2 root root 4096 May 21 14:00 uploads\n</code></pre></div>\n<p>All other <code>wp-content</code> subdirectories (<code>plugins</code>, <code>themes</code>, <code>cache</code>, <code>upgrade</code>) are correctly owned by <code>www-data:www-data</code>.</p>\n<h3>Root Cause</h3>\n<p>This is a well-known Docker volume behavior. When Docker creates a <strong>named volume</strong> and mounts it into a container, if the mount point directory doesn't already exist in the container image (or exists but gets overwritten by the volume mount), Docker initializes the volume as <code>root:root</code> <sup class=\"trigger\"><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56#:~:text=coop-cloud/wordpress%208%200,Continue%3F%20No%20Yes\">2</a><span class=\"tooltip top center citation\"><a href=\"https://github.com/docker-library/wordpress/issues/436#:~:text=It%20seems%20we,depends_on%3A%20-%20d&text=wordpress%3A%20depends_on%3A%20-,restart%3A%20always%20volumes&text=The%20parent%20wp-content,needs%20to%20be&text=It%20seems%20we,owned%20by%20root.\"><span class=\"source\"><img alt=\"\" src=\"https://p.kagi.com/proxy/favicons?c=lQ30-cxV_E2Vdyqb6hrbuYgId7qu7hecjO7o1T7aqQDFc7XpcLttL1Ryala-GNHJye4yFtnhZ7b3uL_uDtQjv6zCLPFWusm4lvLb6SKPCfI%3D\"> github.com</span><b>1. wp-content is owned by root on creation · Issue #436...</b></a><span class=\"passage\">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: - d...wordpress: depends_on: - db image: wordpress:latest container_name: wordpress restart: always volumes ... 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 ...</span></span></sup> <sup class=\"trigger\"><a href=\"https://github.com/docker-library/wordpress/issues/436#:~:text=It%20seems%20we,depends_on%3A%20-%20d&text=wordpress%3A%20depends_on%3A%20-,restart%3A%20always%20volumes&text=The%20parent%20wp-content,needs%20to%20be&text=It%20seems%20we,owned%20by%20root.\">1</a><span class=\"tooltip top center citation\"><a href=\"https://kaspars.net/blog/docker-volume-permissions#:~:text=The%20goal%20of,owned%20by%20root%3Aroot.&text=Turns%20out%20that,permissions.WORDPRESS_DB_PASSWORD%3A%20password%20%23.&text=The%20goal%20of,owned%20by%20root%3Aroot.\"><span class=\"source\"><img alt=\"\" src=\"https://p.kagi.com/proxy/favicons?c=lQ30-cxV_E2Vdyqb6hrbuYgId7qu7hecjO7o1T7aqQDFc7XpcLttL1Ryala-GNHJTBJZYOWVnrywQR_i3xP7pwwPwKDfoI_qzW68G9GBq_4%3D\"> kaspars.net</span><b>3. Docker Volume Permissions (Development, WordPress) — Kaspars Dambis</b></a><span class=\"passage\">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. ... 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 ...</span></span></sup>.</p>\n<p>The specific mechanism in the official WordPress Docker image:</p>\n<ol>\n<li>The image's entrypoint (<code>docker-entrypoint.sh</code>) copies WordPress from <code>/usr/src/wordpress</code> to <code>/var/www/html</code> and runs <code>chown</code> on the destination — but <strong>only on files that exist at the time the entrypoint runs</strong>.</li>\n<li>The <code>wp-content/uploads</code> directory <strong>does not exist</strong> in the upstream WordPress source tarball. It's created by WordPress at runtime when a user first uploads a file.</li>\n<li>When a named Docker volume is mounted at <code>/var/www/html/wp-content/uploads</code>, Docker creates the mount point as <code>root:root</code> <strong>before</strong> the entrypoint executes. Since the directory is a volume mount point, the entrypoint's <code>chown</code> either skips it or the volume initialization happens after the chown.</li>\n<li>The result: <code>uploads</code> ends up owned by <code>root</code>, while directories that <em>do</em> exist in the source image (like <code>plugins</code>, <code>themes</code>) get properly chowned to <code>www-data</code>.</li>\n</ol>\n<p>This is the same issue reported in <code>docker-library/wordpress#436</code> where <code>wp-content</code> is owned by root on creation when subdirectories are mounted as separate volumes <sup class=\"trigger\"><a href=\"https://git.coopcloud.tech/coop-cloud/wordpress/issues/56#:~:text=coop-cloud/wordpress%208%200,Continue%3F%20No%20Yes\">2</a><span class=\"tooltip top center citation\"><a href=\"https://github.com/docker-library/wordpress/issues/436#:~:text=It%20seems%20we,depends_on%3A%20-%20d&text=wordpress%3A%20depends_on%3A%20-,restart%3A%20always%20volumes&text=The%20parent%20wp-content,needs%20to%20be&text=It%20seems%20we,owned%20by%20root.\"><span class=\"source\"><img alt=\"\" src=\"https://p.kagi.com/proxy/favicons?c=lQ30-cxV_E2Vdyqb6hrbuYgId7qu7hecjO7o1T7aqQDFc7XpcLttL1Ryala-GNHJye4yFtnhZ7b3uL_uDtQjv6zCLPFWusm4lvLb6SKPCfI%3D\"> github.com</span><b>1. wp-content is owned by root on creation · Issue #436...</b></a><span class=\"passage\">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: - d...wordpress: depends_on: - db image: wordpress:latest container_name: wordpress restart: always volumes ... 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 ...</span></span></sup>.</p>\n<h3>Workaround (from the issue)</h3>\n<div class=\"codehilite\"><span class=\"filename\">Bash</span><pre><span></span><code><span class=\"nf\">abra</span> app run foo.bar app bash\n<span class=\"nf\">chown</span> <span class=\"no\">-R</span> www-data:www-data wp-content/uploads/\n</code></pre></div>\n<h3>Proper Fixes</h3>\n<p>There are several approaches to fix this permanently:</p>\n<p><strong>1. Add a chown to the entrypoint/custom entrypoint script:</strong></p>\n<div class=\"codehilite\"><span class=\"filename\">Bash</span><pre><span></span><code><span class=\"c\"># In a custom entrypoint or init script, before WordPress starts:</span>\n<span class=\"nf\">chown</span> <span class=\"no\">-R</span> www-data:www-data /var/www/html/wp-content/uploads 2<span class=\"o\">></span>/dev/null || <span class=\"nf\">true</span>\n</code></pre></div>\n<p><strong>2. Ensure the <code>uploads</code> directory exists in the image with correct ownership:</strong><br>\nIn a custom Dockerfile:</p>\n<div class=\"codehilite\"><span class=\"filename\">Dockerfile</span><pre><span></span><code><span class=\"k\">FROM</span> wordpress<span class=\"o\">:</span>latest\n<span class=\"k\">RUN</span> <span class=\"nf\">mkdir</span> <span class=\"no\">-p</span> /var/www/html/wp-content/uploads <span class=\"o\">&&</span><span class=\"nf\"></span> \\\n <span class=\"nf\">chown</span> www-data:www-data /var/www/html/wp-content/uploads\n</code></pre></div>\n<p>This ensures the directory exists <em>before</em> the volume is mounted, so Docker will copy the ownership from the image into the volume on first creation.</p>\n<p><strong>3. Use an init container or deploy hook:</strong><br>\nIn the Co-op Cloud <code>compose.yml</code>, add a deploy command that fixes permissions:</p>\n<div class=\"codehilite\"><span class=\"filename\">Yaml</span><pre><span></span><code><span class=\"s\">app</span><span class=\"p\">:</span>\n <span class=\"s\">image</span><span class=\"p\">:</span> <span class=\"s\">wordpress</span>\n <span class=\"s\">command</span><span class=\"p\">:</span> <span class=\"s\"><span class=\"p\">></span>\n <span class=\"nf\">sh</span> <span class=\"no\">-c</span> <span class=\"s\">\"chown -R www-data:www-data /var/www/html/wp-content/uploads 2>/dev/null;\n apache2-foreground\"</span>\n</span></code></pre></div>\n<p><strong>4. Set <code>UPLOADS_USER</code> / custom <code>WP_CONTENT_DIR</code> approach:</strong><br>\nSome forks of the WordPress image handle this by explicitly creating and chowning the uploads directory in the entrypoint.</p>\n<h3>Summary</h3>\n<table>\n<thead>\n<tr>\n<th>Aspect</th>\n<th>Detail</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Bug</strong></td>\n<td><code>wp-content/uploads</code> owned by <code>root:root</code> on new deploys</td>\n</tr>\n<tr>\n<td><strong>Cause</strong></td>\n<td>Docker named volumes are initialized as <code>root:root</code>; <code>uploads</code> dir doesn't exist in the WordPress image source, so the entrypoint's <code>chown</code> never touches it</td>\n</tr>\n<tr>\n<td><strong>Impact</strong></td>\n<td>WordPress GUI file uploads fail (permission denied)</td>\n</tr>\n<tr>\n<td><strong>Quick fix</strong></td>\n<td><code>chown -R www-data:www-data uploads/</code> inside the container</td>\n</tr>\n<tr>\n<td><strong>Permanent fix</strong></td>\n<td>Ensure <code>uploads</code> dir exists with correct ownership in the image, or add a chown step to the entrypoint/init script</td>\n</tr>\n</tbody>\n</table>","md":"## Investigation: Co-op Cloud WordPress Issue #56 — `uploads` folder owned by `root`\n\n### The Problem\n\nOn new Co-op Cloud WordPress deploys, the `wp-content/uploads` directory is owned by `root:root` instead of `www-data:www-data`, which breaks file uploads in the WordPress GUI [^2].\n\n```\ndrwxr-xr-x 2 root root 4096 May 21 14:00 uploads\n```\n\nAll other `wp-content` subdirectories (`plugins`, `themes`, `cache`, `upgrade`) are correctly owned by `www-data:www-data`.\n\n### Root Cause\n\nThis is a well-known Docker volume behavior. When Docker creates a **named volume** and mounts it into a container, if the mount point directory doesn't already exist in the container image (or exists but gets overwritten by the volume mount), Docker initializes the volume as `root:root` [^1] [^3].\n\nThe specific mechanism in the official WordPress Docker image:\n\n1. The image's entrypoint (`docker-entrypoint.sh`) copies WordPress from `/usr/src/wordpress` to `/var/www/html` and runs `chown` on the destination — but **only on files that exist at the time the entrypoint runs**.\n2. The `wp-content/uploads` directory **does not exist** in the upstream WordPress source tarball. It's created by WordPress at runtime when a user first uploads a file.\n3. When a named Docker volume is mounted at `/var/www/html/wp-content/uploads`, Docker creates the mount point as `root:root` **before** the entrypoint executes. Since the directory is a volume mount point, the entrypoint's `chown` either skips it or the volume initialization happens after the chown.\n4. The result: `uploads` ends up owned by `root`, while directories that *do* exist in the source image (like `plugins`, `themes`) get properly chowned to `www-data`.\n\nThis is the same issue reported in `docker-library/wordpress#436` where `wp-content` is owned by root on creation when subdirectories are mounted as separate volumes [^1].\n\n### Workaround (from the issue)\n\n```bash\nabra app run foo.bar app bash\nchown -R www-data:www-data wp-content/uploads/\n```\n\n### Proper Fixes\n\nThere are several approaches to fix this permanently:\n\n**1. Add a chown to the entrypoint/custom entrypoint script:**\n```bash\n# In a custom entrypoint or init script, before WordPress starts:\nchown -R www-data:www-data /var/www/html/wp-content/uploads 2>/dev/null || true\n```\n\n**2. Ensure the `uploads` directory exists in the image with correct ownership:**\nIn a custom Dockerfile:\n```dockerfile\nFROM wordpress:latest\nRUN mkdir -p /var/www/html/wp-content/uploads && \\\n chown www-data:www-data /var/www/html/wp-content/uploads\n```\nThis ensures the directory exists *before* the volume is mounted, so Docker will copy the ownership from the image into the volume on first creation.\n\n**3. Use an init container or deploy hook:**\nIn the Co-op Cloud `compose.yml`, add a deploy command that fixes permissions:\n```yaml\napp:\n image: wordpress\n command: >\n sh -c \"chown -R www-data:www-data /var/www/html/wp-content/uploads 2>/dev/null;\n apache2-foreground\"\n```\n\n**4. Set `UPLOADS_USER` / custom `WP_CONTENT_DIR` approach:**\nSome forks of the WordPress image handle this by explicitly creating and chowning the uploads directory in the entrypoint.\n\n### Summary\n\n| Aspect | Detail |\n|---|---|\n| **Bug** | `wp-content/uploads` owned by `root:root` on new deploys |\n| **Cause** | Docker named volumes are initialized as `root:root`; `uploads` dir doesn't exist in the WordPress image source, so the entrypoint's `chown` never touches it |\n| **Impact** | WordPress GUI file uploads fail (permission denied) |\n| **Quick fix** | `chown -R www-data:www-data uploads/` inside the container |\n| **Permanent fix** | Ensure `uploads` dir exists with correct ownership in the image, or add a chown step to the entrypoint/init script |","profile":{"id":"095899c5-634b-4eeb-ad03-41fd508fbb51","name":"Code","accessible":true,"subtitle":"Fast, straight answer (<5 secs)","model":"ki_quick","deprecate":false,"retired":false,"successor_model":null,"model_name":"Quick","model_provider":"kagi","model_input_limit":1000000,"recommended":false,"model_info":"\n\n\n\n<div class=\"heading\">\n <div class=\"left\">\n \n \n \n <svg class=\"model-icon icon-xs\" data-model=\"ki_quick\">\n <use href=\"#icon-kagi-1\"/>\n </svg>\n <h3>\n \n <span class=\"model-name\">Code</span>\n </h3>\n </div>\n\n \n</div>\n\n\n<hr />\n<ul>\n \n <li>\n <h4>\n Model\n </h4>\n <span class=\"flex align-center gap-8\">\n <svg class=\"icon-xs icon\">\n <use href=\"#icon-kagi\" />\n </svg>\n <span class=\"model-name\">Quick</span>\n </span>\n </li>\n <li>\n <h4>\n Web access\n </h4>\n <span>On</span>\n </li>\n <li>\n <h4>\n Lenses\n </h4>\n <span>--</span>\n </li>\n \n</ul>\n\n\n","model_provider_name":"Kagi","internet_access":true,"personalizations":false,"shortcut":null,"is_default_profile":true},"metadata":"<li>\n <span class=\"attribute\">Model</span>\n <span class=\"value\"><i class=\"icon-xs mr-4\"><svg><use href=\"#icon-kagi\"></use></svg></i>Quick</span>\n</li>\n<li>\n <span class=\"attribute\">Version</span>\n <span class=\"value\">ki_quick-2025-11-18</span>\n</li>\n<li>\n <span class=\"attribute\">Speed (tok/s)</span>\n <span class=\"value\">67</span>\n</li>\n<li>\n <span class=\"attribute\">Tokens</span>\n <span class=\"value\">11,141</span>\n</li>\n<li>\n <span class=\"attribute\">Cost / Total ($)</span>\n <span class=\"value\">0.04 / 0.04</span>\n</li>\n<li>\n <span class=\"attribute\">End to end time (s)</span>\n <span class=\"value\">35.67</span>\n</li>\n<li>\n <span class=\"attribute\">Submitted</span>\n <span class=\"value\">2026-05-26 09:00</span>\n</li>\n\n","documents":[]}]