Permissions were broken after transferring a backup to Windows

Hi everyone, I copied the Seafile-data folder to a Windows machine as a backup, without the tar.gz file. I admit it was my mistake. As a result, after moving it back, the permissions for avatars, wallpapers, logos, and everything running on www-data (Nginx) were broken. If I recursively grant permissions to /shared inside the container, everything works again, but only until the container is restarted, of course, because scripts requiring root can’t run on startup.
I found that Nginx has a /media location and contains simlynks - avatars and custom, and www-data is getting “Permission denied” for them. Could you explain how the media read permissions are implemented?

Based on your description, the issue is caused by the way Windows handles file metadata. When you copied the folders directly to a Windows machine without using an archive format (like .tar.gz), you lost two critical pieces of information: Linux Ownership/Permissions (UID/GID) and Symbolic Links.

When you moved the files back, they likely inherited the permissions of the user who performed the copy, causing Nginx (running as www-data) to lose access.

Here are the points you should check to fix this permanently:

1. Fix Broken Symbolic Links

The Permission denied errors in the media folder are often because the symlinks for avatars and custom were converted into regular folders or corrupted during the Windows transfer.

  • Check the /opt/seafile/seafile-server-latest/seahub/media directory.
  • Verify if avatars and custom are actual symlinks pointing to your /shared/seafile/seahub-data/ directory.
  • If they are regular folders, delete them and recreate the symlinks manually using ln -s.

2. Correct Ownership (chown vs chmod)

Recursive chmod allows access, but if the ownership is wrong, certain Seafile operations or startup scripts may still fail. You need to ensure the files are owned by the user running the service inside the container.

  • Run a recursive ownership change: chown -R www-data:www-data /shared (Note: If you are using the NON_ROOT setup, the owner should be the user with UID 8000).

3. Directory Traversal Permissions

Nginx needs the execute (+x) permission on every parent directory in the path to access a file.

  • Ensure that /shared, /shared/seafile, and /shared/seafile/seahub-data all have at least drwxr-xr-x permissions. If any parent directory is missing the x bit for the “others” or “group” category, Nginx will return a 403 or 404.

4. Why it breaks on restart

The Seafile container startup scripts often check the environment and may attempt to fix permissions or initialize components. If the underlying symlink structure is broken, the initialization might fail or reset permissions to a state that doesn’t account for your manual changes.

Tip for the future: Always wrap the seafile-data or shared folder in a .tar or .zip file before moving it to a Windows environment to preserve the Linux filesystem attributes.

Yes, that’s what I did initially, granting www-data -R permissions to /shared, and as I described above, it worked. Initially, the container ran as admin:administrator (it’s a QNAP NAS), and the container starts as admin:administrator. After changing admin:administrator permissions to www-data, there are two problems:

  • Any action within the UI to change the logo, wallpaper, avatar, etc. creates an object within avatar\custom as admin:administrator, which immediately breaks the permissions for www-data.
  • If I chown -R admin:administrator /shared, after rebooting the container, it crashes and won’t start.

after container restart I get

[2026-04-13 07:42:30] Skip running setup-seafile-mysql.py because there is existing seafile-data folder.

Error: the user running the script (“root”) is not the owner of “/shared/seafile/seafile-data” folder, you should use the user “www-data” to run the script.

Traceback (most recent call last):

File “/scripts/start.py”, line 91, in

main()

File “/scripts/start.py”, line 76, in main

call('{} start'.format(get_script('seafile.sh')))

File “/scripts/utils.py”, line 71, in call

return subprocess.check_call(\*a, \*\*kw)

       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/usr/lib/python3.12/subprocess.py”, line 413, in check_call

raise CalledProcessError(retcode, cmd)

subprocess.CalledProcessError: Command ‘/opt/seafile/seafile-server-13.0.20/seafile.sh start’ returned non-zero exit status 255.

###################################################################

since above we changed the owner to www-data

But when I return the owner to admin:administrator, which is equivalent to root:root inside the container, everything starts working. I need to somehow specifically grant rights for ngixn www-data, and not the entire -R /shared