Recognizing the problem
This guide is for the classic Docker bind-mount failure:
- the container starts, but cannot write under
/data - plugins, configs, or world files fail to update
- logs mention
Permission denied
With itzg/minecraft-server, this usually comes down to one mismatch:
the host filesystem ownership does not match the user that runs the Minecraft process inside the container.
Why this happens
By default, the image switches to UID 1000 and GID 1000. If your bind-mounted host directory belongs to a different user or group, writes can fail.
That is common when:
- the directory was created by
root - the server was migrated from another host
- the host user has a different UID than 1000
Quick fix
If you intentionally want to keep the default container user, fix the ownership on the host:
sudo chown -R 1000:1000 ./data
Then recreate or restart the stack:
docker compose up -d
Option 1: Keep the default container user and fix the host
This option keeps the image’s default user and changes ownership of the host directory.
Compose:
services:
mc:
image: itzg/minecraft-server:latest
volumes:
- ./data:/data
Host fix:
sudo chown -R 1000:1000 ./data
find ./data -maxdepth 2 -ls | head
This keeps the image on its documented default.
Option 2: Match the container user to your host user
If your host directory should stay owned by a different user, set UID and GID explicitly:
services:
mc:
image: itzg/minecraft-server:latest
environment:
UID: "1001"
GID: "1001"
volumes:
- ./data:/data
This is useful when you already standardized file ownership on the host and want the container to follow it.
Option 3: Use Compose user only when you mean it
Docker also supports a Compose-level user: setting:
services:
mc:
user: "1001:1001"
That works, but it changes the container-level user behavior more broadly.
If your goal is simply to align with the image's supported ownership model, UID and GID are usually clearer.
How to validate the fix
After restarting the stack:
docker compose logs -f
You want to see normal startup without repeated permission failures. Then test a write path, for example:
- change
server.properties - add a plugin
- trigger
save-allthrough RCON
If the server can write under /data, the fix is complete.
Common mistakes
chmod -R 777
Mode 777 gives every user write access. Correct the affected directory’s owner and group instead.
Mismatched user and group IDs
Use matching IDs when configuring:
- default
1000:1000 - custom
UID/GID - Compose
user
The container user must have write access to the data directory.
Forgetting SELinux or rootless specifics
If you use Podman or an SELinux/AppArmor-heavy setup, the bind mount may need extra labeling such as :Z.
That is a different class of failure than plain UID/GID mismatch.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Permission denied on /data | Host ownership mismatch | Align ownership with container UID/GID |
Files were created as root | Earlier commands used sudo or root container | Correct ownership recursively |
| Config still will not change | Container recreated with old mount or old env | Re-run docker compose up -d and inspect the effective config |
| Admin commands work, file writes fail | RCON path is fine, filesystem is not | Focus on bind mount ownership, not networking |