Docker Permissions Demystified
chown: changing ownership of '/volume1/docker/myapp/data': Operation not permitted

If you’ve run Docker on a Synology NAS, you’ve seen this error. If you’re like me, you’ve also spent hours trying to fix it before realizing it’s not actually breaking anything.

Docker permissions on NAS devices are different from Docker on a regular Linux server. Here’s everything I learned the hard way.

The Problem

I was setting up ruTorrent containers on my Synology. The compose file looked standard:

services:
  rutorrent:
    image: crazymax/rtorrent-rutorrent
    container_name: rutorrent
    environment:
      - PUID=1026
      - PGID=100
    volumes:
      - /volume1/docker/rutorrent:/data
    ports:
      - "8080:8080"

Started the container. Got a wall of permission errors. Container was running anyway. Files were being created with wrong ownership. Authentication wasn’t working. Everything was broken.

Understanding PUID and PGID

Most Docker images that deal with files support PUID and PGID environment variables. These tell the container what user and group ID to use when creating files.

On a standard Linux system, you’d match these to your user:

id username
# uid=1000(username) gid=1000(username) groups=1000(username)

On Synology, user IDs start at 1024 or higher. The default admin user isn’t UID 1000. And there’s a “users” group (GID 100) that most files should belong to.

To find your Synology user’s IDs:

id your_username
# uid=1026(your_username) gid=100(users)

Those are the numbers that go in PUID and PGID. But setting them correctly is only half the battle.

Why chown Fails on Synology

Synology’s DSM uses a modified Linux with extra permission restrictions. Regular users can’t change file ownership, even on files they own. Only root can chown.

Docker containers typically run a startup script that does something like:

chown -R $PUID:$PGID /data

This works on normal Linux. On Synology, it fails with “Operation not permitted” because the container isn’t running as root on the host—it’s running as a user without chown privileges.

The errors are misleading. The container usually still works. It’s just complaining about something it can’t do but doesn’t actually need to do.

The Real Fix: Correct Ownership Before Starting

Instead of letting the container try (and fail) to fix permissions, set them correctly before starting:

# On Synology, via SSH
sudo chown -R 1026:100 /volume1/docker/rutorrent
sudo chmod -R 755 /volume1/docker/rutorrent

Now when the container starts, the files already have the right ownership. The chown command inside the container still fails, but there’s nothing to fix.

The Port Conflict Problem

While debugging permissions, I hit another classic:

Error starting userland proxy: listen tcp4 0.0.0.0:8081: bind: address already in use

Docker couldn’t bind the port because something else was using it. On Synology, many ports are already taken by DSM services you might not know about.

Find what’s using a port:

netstat -tunlp | grep 8081

Or just pick ports above 8100 to avoid conflicts with Synology’s built-in services. I ended up using 8091, 8092, etc. for my containers.

Authentication: htpasswd Files

The ruTorrent image uses htpasswd files for authentication. These need to exist before the container starts, with correct permissions:

# Create password directory
mkdir -p /volume1/docker/rutorrent/passwd

# Generate htpasswd file (using Docker to get htpasswd binary)
docker run --rm httpd:2.4-alpine htpasswd -Bnb myuser mypassword > /volume1/docker/rutorrent/passwd/rutorrent.htpasswd

# Set permissions
sudo chown 1026:100 /volume1/docker/rutorrent/passwd/rutorrent.htpasswd
chmod 644 /volume1/docker/rutorrent/passwd/rutorrent.htpasswd

The -B flag uses bcrypt hashing. The -n flag outputs to stdout instead of updating a file. The -b flag takes the password from the command line (convenient but visible in shell history—use -i for interactive input on production systems).

The Complete Docker Compose

After all the debugging, here’s what actually works:

version: "3.9"
services:
  rutorrent:
    image: crazymax/rtorrent-rutorrent
    container_name: rutorrent
    ports:
      - "6881:6881/udp"
      - "8091:8080"
      - "51413:50000"
    environment:
      - TZ=America/Denver
      - PUID=1026
      - PGID=100
    volumes:
      - /volume1/docker/rutorrent/data:/data
      - /volume1/docker/rutorrent/downloads:/downloads
      - /volume1/docker/rutorrent/passwd:/passwd
    restart: unless-stopped

The key differences from a generic setup:

  1. PUID/PGID match my Synology user (found via id username)
  2. Ports are above 8100 to avoid Synology conflicts
  3. Password directory is mounted at /passwd where the image expects it
  4. All directories pre-created with correct ownership

Pre-flight Checklist

Before running any Docker container on Synology:

# 1. Create directories
mkdir -p /volume1/docker/myapp/{data,config}

# 2. Set ownership to your user
sudo chown -R $(id -u):$(id -g) /volume1/docker/myapp

# 3. Set permissions
chmod -R 755 /volume1/docker/myapp

# 4. Find your PUID/PGID
id
# Use these values in docker-compose.yml

# 5. Check for port conflicts
netstat -tunlp | grep :8080

Do this before docker-compose up and you’ll skip most permission headaches.

When Containers Actually Need Root

Some containers legitimately need root access—typically for:

  • Binding to ports below 1024
  • Accessing hardware devices
  • Running system services like VPNs

For these, you might need privileged mode:

services:
  vpn-client:
    image: some-vpn-image
    privileged: true
    cap_add:
      - NET_ADMIN

But use this sparingly. Most applications don’t need it, and it weakens container isolation.

The Synology-Specific Gotchas

Volume paths are different. Synology uses /volume1/, /volume2/, etc. Not /data or /mnt.

User IDs start higher. Expect UIDs in the 1024+ range, not 1000.

GID 100 is special. The “users” group includes most regular users. Use it unless you have a reason not to.

DSM updates can break things. Major DSM updates sometimes reset Docker configurations. Keep your compose files backed up.

The Docker package location matters. Install Docker from Synology’s Package Center, not manually. It integrates with DSM’s permissions system.

The Debugging Flow

When a container won’t start or behaves strangely:

  1. Check the logs first

    docker logs container_name
  2. Verify directory permissions

    ls -la /volume1/docker/myapp/
  3. Confirm PUID/PGID match

    id your_username
  4. Test port availability

    netstat -tunlp | grep :yourport
  5. Try running interactively

    docker run -it --rm image_name /bin/sh

Nine times out of ten, it’s permissions or ports.

The Lesson

Docker permission errors on Synology are usually warnings, not failures. The container is complaining about things it can’t do, but those things often don’t matter if you’ve set up the directories correctly beforehand.

Set ownership before starting containers. Use your actual Synology UID/GID. Pick ports above 8100. And learn to ignore the chown errors that don’t actually break anything.


The ruTorrent containers have been running for months now. The permission errors still appear in the logs every restart. I’ve stopped caring.