How to upgrade with compose and docker volumes

Having just migrated from nextcloud bare-metal to nextcloud docker-compose, I have now just run into my first upgrade, which fails due to Operation not permitted and permission denied errors.

In my setup, I have /var/www/html as a docker volume. The docker volume internally has the permissions ‘www-data:www-data’ for all nextcloud/php files. My data volume is /var/www/data thus isolated from the html bits.

During start of nextcloud, it wants to upgrade from to which internally uses ‘rsync’ from the entrypoint to copy the installation files to my volume. But this is where the problems start, because the container is doing this step as root, instead of www-data, it now cannot copy/create files anymore. Changing the volume (from the /var/lib/docker/volumes/…/_data) to root:root (but keeping www-data on config.php), adding o+w and running the container with 'cap-add fowner,chown.

This gets me through the upgrade, but have to then ‘undo’ my chown/o+w-ing, and start NC again.

However, I then am greeted with nextcloud-init-sync.lock; but I may have had this issue before as well as I found o+w bit to have been set on the root of my volume … though I have safely removed that again as it’s enough to just have the file with root:root permissions. This does indicate that the volume needs access for two users, www-data for the php files during ‘runtime’ and root for nextcloud-init-sync.lock for startup.

I’m sure this isn’t the way/intended behavior is it? What am I doing wrong?

P.S. No idea why or how or even if important, but I’m also getting
Warning: /var/www/html/config/autoconfig.php differs from the latest version of this image at /usr/src/nextcloud/config/autoconfig.php
Warning: /var/www/html/config/redis.config.php differs from the latest version of this image at /usr/src/nextcloud/config/redis.config.php
Warning: /var/www/html/config/s3.config.php differs from the latest version of this image at /usr/src/nextcloud/config/s3.config.php
Warning: /var/www/html/config/smtp.config.php differs from the latest version of this image at /usr/src/nextcloud/config/smtp.config.php
Warning: /var/www/html/config/upgrade-disable-web.config.php differs from the latest version of this image at /usr/src/nextcloud/config/upgrade-disable-web.config.php
as part of my upgrade. I can just copy these files no problem, but wondered why the upgrade didn’t do this itself. Those files are mostly not existing …

I can’t follow the problem well. Please review Docker upgrade procedure. By default the container runs as root and starts the webserver as www-data. I’m doing such upgrade since years without issues.

1 Like

Please post your actual Docker Compose. Are you running a customized image?

I can just copy these files no problem, but wondered why the upgrade didn’t do this itself. Those files are mostly not existing …

This is a new bit of code that detects if you have out-of-date config files in your installation. The docs describing what it means and what to do about it are here.

1 Like

So that doc lists as upgrade steps:

$ docker-compose pull
$ docker-compose up -d

I’m pretty sure I can’t mess that up much :stuck_out_tongue:

But no, not running a customized image. My compose config is the following:

networks:
  nextcloud: {}

volumes:
  nextcloud:

services:
  nextcloud:
    image: docker.io/library/nextcloud:apache
    cap_add:
      - setgid
      - setuid
    cap_drop:
      - all
    depends_on:
      - postgresql
      - redis
    env_file:
      - common.env
      - ./nextcloud/server.env
    volumes:
      - postgresql_socket:/run/postgresql:rw
      - redis_socket:/run/redis:rw
      - ./nextcloud/user.ini:/var/www/html/.user.ini:rw
      - nextcloud:/var/www/html:rw
      - /srv/nextcloud_userdata:/var/www/data:rw
      - ./nextcloud/opcache.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini:ro
    networks:
      - nextcloud
      - nginx
    expose:
      - "80/tcp"
    restart: unless-stopped
    healthcheck:
      test: runuser --user www-data -- php -f /var/www/html/cron.php
      interval: 5m
      timeout: 10m
      start_period: 1m

While I want to upgrade to the fpm image at some point, this is what I have right now. Redis I want to change for the foss thing also at some point …

In common.env, there’s nothing more then TZ='Europe/Amsterdam' and in server.env I have

PHP_UPLOAD_LIMIT="16G"
PHP_MEMORY_LIMIT="8G"
VIRTUAL_HOST="nextcloud.mydomain.com"

Where VIRTUAL_HOST is used by docker-gen, but not relevant to this container itself.

opcache.ini was my trying to solve nextcloud setupcheck complaining about opcache. Haven’t gotten this to work, as I change values, it keeps complaining. So I left it at this for now.

opcache.enable=1
opcache.max_accelerated_files=40000
opcache.memory_consumption=65536
opcache.interned_strings_buffer=4092
opcache.save_comments=1
opcache.revalidate_freq=60
opcache.jit=1255
opcache.jit_buffer_size=256M

and finally user.ini, like server.env to increase large files being able to be uploaded.

mbstring.func_overload=0
always_populate_raw_post_data=-1
default_charset='UTF-8'
output_buffering=0
max_input_time=3600
max_execution_time=3600

But this is probably all un-relevant. The upgrade fails because the entrypoint script cannot rsync the files to /var/www/html. This is because as per migration guide

docker-compose exec app chown -R www-data:www-data /var/www/html/config

We should chown everything to www-data. Obviously, while checking this again right now, I noticed that maybe I was a bit to overzealous with my chowning. I changed ownership of /var/www/html instead of just config and custom_apps. So i’ll switch the standard nextcloud files to root:root! It makes sense, nobody should be able to edit those files …

Lets see if the next upgrade will cause the same issues with root:root permissions.

Btw, I am aware of the autoconfiguration scripts. I even wrote a patch once that I failed to submit :stuck_out_tongue: but this could be due to the same reason as above. Wrong permissions caused things to fail.

Probably no dev reading this; but maybe it’s an idea to split out config (and custom apps, but I’m sure that’s already possibly) like data so we can keep the config isolated from the code. Permissions become more obvious and easy to handle in that case. I do get that it’s tricky because config.php holds the locations of folders …

that’s true but many people don’t read/follow the docs for this reason we always ask!

I was curious if cap_add/cap_drop order might be a problem but seems to be OK according to Docker-Compose: order of cap_drop and cap_add? - #3 by thediveo - Compose - Docker Community Forums this is the most noticeable difference from default - check if it works without this directive in the future

I would expect root can rsync files of another user but not other way round. As you can see in my guide Nextcloud docker-compose setup with notify_push (2024) apps, config and data are owned by the limited container user (which IMHO translates to www-data by default)

you are right it is possible (see the guide above) or container docs > additional volumes

Will try. Ideally we’d drop all caps; but those two are the ones needed for the entrypoint to run as root; and then drop to the www-data user …

It can; however in docker, on a volume specifically, due to the user-remapping; you end up with the different then expected permissions.

In other words; the container runs as root+offset and on a volume that means, it cannot access files owned by www-data, it can only do things with the ‘other’ user. So running it is fine, but rsyncing it is not.

My guess is, with everything owned as root:root in /var/www/html and only config and custom-apps being owned by www-data it should be fine again; because those files are not owned by root:root but root+offset:root+offset and thus rsync should have no problem rsyncing things …

I’m curious about root+offset could you please explain in detail what the exact scenario? is it something like Isolate containers with a user namespace | Docker Docs or something similar?

So the way docker works is exactly what you linked.

If you use --privileged no remapping of the namespace happens, and root == root. uid 1000 == uid 1000.

But without that flag, your container will get a namespace offset, unique to each container. something like +10000 or the like. So root is really uid 10000, but within the container, it is really 0. This is fine, until you pull in a docker volume. The files on the filesystem gets certain permissions assigned and these are likely not to match. For example files installed with e.g. www-data. I think, but not 100% certain, that the mapping happens slightly different on actual volumes (vs for example --volume mounts), for at least the user root anyway.