[{"data":1,"prerenderedAt":178},["ShallowReactive",2],{"seo-verification":3,"blog-docker-volume-permissions-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-docker-volume-permissions-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":135,"ctaBody":136,"ctaButton":137,"ctaUrl":138,"relatedPosts":139},395,"docker-volume-permissions-vps",{"fr":12,"en":10,"ar":13,"es":14},"docker-volume-permissions-self-hosted-vps","اذونات-مجلدات-docker-vps","permisos-volumenes-docker-vps","Docker volumes: fix permissions in 3 commands","Permission denied in your Docker logs? Understand the UID\u002FGID mechanism and fix volume permissions without ever running as root.",10,0,false,"2026-09-28T00:00:00+00:00","2026-09-29T14:40:42+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},3,"Deployment","deploiement","bg-success\u002F10 text-success",[28],{"id":23,"name":24,"slug":25,"color":26,"icon":25},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fdocker-volume-permissions-self-hosted-vps-poster.svg","The app starts, the container runs, but logs show `Permission denied` and nothing gets saved. This issue affects virtually every self-hosted application the moment you mount a host directory into Docker. The cause is always the same: the UID of the user running inside the container doesn't match the owner of the directory on the host. This guide explains the mechanism, gives diagnostic commands, and provides the right fix for each scenario — without ever running your containers as root.",[34,38,41,49,52,70,73,90,93,96,120,123,129,132],{"type":35,"title":36,"body":37},"h2","The mechanism: why Docker refuses to write","Docker shares the host Linux kernel. When a container mounts a host directory (bind-mount), the filesystem applies the same POSIX permission rules. A file owned by UID 1000 on the host remains owned by UID 1000, regardless of whether it's called `alice` on the host or `paperless` in the container. If the process in the container runs as UID 472 and the directory is owned by root (UID 0), writes are denied — even with `chmod 755`.\n\nClassic trap: you create the directory with your SSH session (UID 1000), then start a Grafana container running as UID 472. Grafana tries to write its SQLite database into `\u002Fvar\u002Flib\u002Fgrafana` mounted from `\u002Fopt\u002Fgrafana\u002Fdata` — which belongs to your SSH user. Result: `GF_PATHS_DATA='\u002Fvar\u002Flib\u002Fgrafana' is not writable.`",{"type":35,"title":39,"body":40},"Most affected applications and their UIDs","Docker permissions issues mainly affect applications that run with a specific UID different from the host's.",{"type":42,"items":43},"ul",[44,45,46,47,48],"**Paperless-ngx** — UID `1000` (user `paperless`): manages `consume`, `export`, `media` and `data` directories","**Grafana** — UID `472` (user `grafana`): writes SQLite database and plugins to `\u002Fvar\u002Flib\u002Fgrafana`","**Nextcloud** (Debian image) — UID `33` (user `www-data`): data directory, config and logs","**Immich** — UID `1000` (user `node`): photo library, thumbnails and ML database","**Gitea** — UID `1000` (user `git`): repositories, SSH keys, logs and default SQLite database",{"type":35,"title":50,"body":51},"Diagnosis: identify the problem in under 2 minutes","Before fixing, confirm it's a permissions problem and identify the UID involved. Three commands are enough.",{"type":53,"steps":54},"steps",[55,58,61,64,67],{"title":56,"body":57},"Read the exact error message","Check the logs of the affected container:\n```\ndocker logs \u003Ccontainer-name> 2>&1 | grep -i 'permission\\|denied\\|cannot\\|mkdir'\n```\nA `permission denied` or `cannot create directory` message confirms the diagnosis.",{"title":59,"body":60},"Identify the process UID inside the container","```\ndocker exec \u003Ccontainer-name> id\n```\nTypical output: `uid=472(grafana) gid=0(root)`. Note the UID — here `472`.",{"title":62,"body":63},"Check the host directory owner","```\nls -ln \u002Fopt\u002Fgrafana\u002Fdata\n```\nOutput `drwxr-xr-x 2 0 0 ...` means the directory is owned by root (UID 0). UID 472 only has \"other\" rights — read and execute, no write.",{"title":65,"body":66},"Use stat for a complete diagnosis","```\nstat \u002Fopt\u002Fgrafana\u002Fdata\n```\nCheck the `Uid:` and `Gid:` lines. If they show `(0\u002Froot)` while your container runs as 472, the problem is confirmed.",{"title":68,"body":69},"Inspect the container configuration","```\ndocker inspect \u003Ccontainer-name> | grep -A5 'Mounts'\n```\nThis command lists all active bind-mounts and named volumes, with their host source and container destination.",{"type":35,"title":71,"body":72},"Fix by case: chown on the host directory","The basic fix is a `chown` of the host directory to the UID expected by the container. Here are the commands for the most common applications.",{"type":53,"steps":74},[75,78,81,84,87],{"title":76,"body":77},"Grafana (UID 472)","```\nmkdir -p \u002Fopt\u002Fgrafana\u002Fdata\nchown -R 472:472 \u002Fopt\u002Fgrafana\u002Fdata\n```\nIn your `compose.yml`:\n```yaml\nvolumes:\n  - \u002Fopt\u002Fgrafana\u002Fdata:\u002Fvar\u002Flib\u002Fgrafana\n```",{"title":79,"body":80},"Nextcloud (UID 33, Debian image)","```\nmkdir -p \u002Fopt\u002Fnextcloud\u002F{data,config,apps}\nchown -R 33:33 \u002Fopt\u002Fnextcloud\u002Fdata\nchown -R 33:33 \u002Fopt\u002Fnextcloud\u002Fconfig\n```\nNote: the Alpine image uses UID 82. Verify with `docker exec \u003Ccontainer> id www-data` if you're unsure which variant you're using.",{"title":82,"body":83},"Paperless-ngx (UID 1000)","```\nmkdir -p \u002Fopt\u002Fpaperless\u002F{consume,export,media,data}\nchown -R 1000:1000 \u002Fopt\u002Fpaperless\u002Fconsume\nchown -R 1000:1000 \u002Fopt\u002Fpaperless\u002Fexport\nchown -R 1000:1000 \u002Fopt\u002Fpaperless\u002Fmedia\nchown -R 1000:1000 \u002Fopt\u002Fpaperless\u002Fdata\n```",{"title":85,"body":86},"Immich (UID 1000)","```\nmkdir -p \u002Fopt\u002Fimmich\u002F{library,thumbnails,encoded-video,profile}\nchown -R 1000:1000 \u002Fopt\u002Fimmich\n```\nNote: `PUID`\u002F`PGID` environment variables do NOT work with official Immich images. They are specific to LinuxServer.io images.",{"title":88,"body":89},"Verify after correction","```\nls -ln \u002Fopt\u002Fgrafana\u002Fdata\n```\nShould show `drwxr-xr-x 2 472 472 ...`. Then restart the container:\n```\ndocker compose restart grafana\ndocker logs grafana --tail 20\n```",{"type":35,"title":91,"body":92},"Variants: bind-mounts, named volumes and user:","The `chown` on the host directory works for bind-mounts, but Docker offers other approaches depending on the context.\n\n**`user:` directive in compose.yml.** Some images are designed to accept an arbitrary UID via `user:`. This avoids `chown` if your directory belongs to your host user:\n```yaml\nservices:\n  app:\n    image: my-image\n    user: \"1000:1000\"\n    volumes:\n      - \u002Fhome\u002Fuser\u002Fdata:\u002Fapp\u002Fdata\n```\nThis approach only works if the image doesn't require internal files owned by a specific UID (SUID binaries, sockets, etc.). Paperless-ngx supports this mode; Grafana does not.\n\n**Named Docker volumes.** With a named Docker volume (`docker volume create`), Docker manages the directory under `\u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F`. On first write, the directory is created with the container process permissions. Result: no permission issues at startup, but migrating existing data requires an explicit copy step via `docker run --rm -v source:\u002Ffrom -v dest:\u002Fto alpine cp -a \u002Ffrom\u002F. \u002Fto\u002F`.",{"type":35,"title":94,"body":95},"NFS and remote mounts","NFS mounts add a layer of complexity: the NFS server applies its own UID rules. If the server exports with `root_squash` (default), root access from the client is reduced to `nobody`. If your container runs as UID 472 and the NFS share has no UID mapping on the server side, you get access denied even after a successful `chown` on the client.\n\nSolutions:\n1. Configure the NFS export with `all_squash,anonuid=472,anongid=472` for Grafana, or the matching UID for your application.\n2. Use `no_root_squash` only if you fully control the network (security risk).\n3. For CIFS\u002FSMB, pass `uid=33,gid=33` in mount options for Nextcloud — CIFS doesn't allow ownership changes after mount.\n\nFor CIFS mounts in `\u002Fetc\u002Ffstab`:\n```\n\u002F\u002Fserver\u002Fshare \u002Fopt\u002Fnextcloud\u002Fdata cifs uid=33,gid=33,credentials=\u002Fetc\u002Fcifs-creds,iocharset=utf8 0 0\n```",{"type":97,"title":98,"headers":99,"rows":103},"comparison","Fix methods: comparison",[100,101,102],"Method","Advantages","Risks \u002F Limitations",[104,108,112,116],[105,106,107],"chown UID:GID on host directory","Simple, universal, compatible with all images","Must know the exact UID; must redo if directory is recreated",[109,110,111],"user: UID:GID in compose.yml","No chown needed; portable between hosts","Image must support arbitrary UIDs; may break internal files",[113,114,115],"Named Docker volumes","Permissions handled automatically on first startup","Less transparent; migrating existing data is more complex",[117,118,119],"--privileged or chmod 777","Immediately fixes the problem","DANGER: exposes host and all processes; never use in production",{"type":35,"title":121,"body":122},"Troubleshooting: 4 classic errors with their exact messages","Here are the four most common Docker errors related to permissions, with the exact error message and its solution.",{"type":42,"items":124},[125,126,127,128],"**`mkdir: cannot create directory '\u002Fvar\u002Flib\u002Fgrafana\u002Fplugins': Permission denied`** → Host directory doesn't belong to UID 472. Apply `chown -R 472:472 \u002Fopt\u002Fgrafana\u002Fdata`.","**`[Errno 13] Permission denied: '\u002Fusr\u002Fsrc\u002Fpaperless\u002Fmedia'`** → Paperless-ngx can't write to its media directory. Verify the bind-mount belongs to UID 1000 on the host.","**`Could not create lock file \u002Fvar\u002Flib\u002Fgrafana\u002F.~lock.grafana.db`** → Grafana can read the directory but not write to it. Permissions issue on existing files: `chown 472:472 \u002Fopt\u002Fgrafana\u002Fdata\u002F*.db` or delete the lock file.","**`chown: changing ownership of '\u002Fdata': Operation not permitted`** (at container startup) → The image tries to fix permissions itself but can't because it doesn't run as root. Perform `chown` manually on the host BEFORE starting the container.",{"type":130,"body":131},"tip","**SELinux and AppArmor: `:z` and `:Z` flags in bind-mounts.** On systems with active SELinux (CentOS, RHEL, Fedora), a bind-mount can be blocked even if POSIX permissions are correct. Docker provides two suffixes: `:z` relabels the content for sharing between multiple containers, and `:Z` relabels for private access (single container). Example: `- \u002Fopt\u002Fgrafana\u002Fdata:\u002Fvar\u002Flib\u002Fgrafana:z`. Without this flag on a SELinux system, you'll get `Permission denied` even after a correct `chown`. To check if SELinux is blocking: `ausearch -m AVC -ts recent | grep docker`.",{"type":35,"title":133,"body":134},"Conclusion","The `Permission denied` in Docker logs is never inevitable. The three-command solution: `docker exec \u003Ccontainer> id` to find the UID, `ls -ln \u003Chost-directory>` to confirm the current owner, then `chown -R \u003CUID>:\u003CGID> \u003Chost-directory>` to fix it. The golden rule: create host directories with the correct owner before starting the container, not after. On a dedicated VPS, this discipline prevents 90% of data silently lost on restart incidents.","A VPS ready for self-hosting","Deploy your Docker applications on a ServOrbit VPS with root access, dedicated IPv4 and automatic snapshots. Starting from 99 DH\u002Fmonth.","See VPS plans","\u002Fvps-cloud",[140,162],{"id":141,"slug":142,"slugs":143,"title":147,"excerpt":148,"readTime":149,"views":150,"isPinned":19,"publishedAt":151,"updatedAt":152,"category":153,"categories":159,"featuredImage":29,"bgImage":30,"posterImage":161,"relatedSolution":29},369,"docker-secrets-production-protecting-credentials-on-vps",{"fr":144,"en":142,"ar":145,"es":146},"docker-secrets-securite-vps-production","اسرار-docker-في-الانتاج-حماية-بيانات-الاعتماد-على-vps","docker-secrets-produccion-proteger-credenciales-vps","Docker Secrets in production: protecting secrets on a VPS","Manage Docker secrets in production without exposing them in docker-compose.yml. Docker Secrets without Swarm, .env.vault and SOPS: method and comparison.",7,1,"2026-09-20T00:00:00+00:00","2026-09-20T21:13:51+00:00",{"id":154,"name":155,"slug":156,"color":157,"icon":158},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[160],{"id":154,"name":155,"slug":156,"color":157,"icon":158},"\u002Fblog\u002Fcovers\u002Fdocker-secrets-securite-vps-production-poster.svg",{"id":163,"slug":164,"slugs":165,"title":169,"excerpt":170,"readTime":171,"views":18,"isPinned":19,"publishedAt":172,"updatedAt":173,"category":174,"categories":175,"featuredImage":29,"bgImage":30,"posterImage":177,"relatedSolution":29},229,"docker-compose-in-production-10-point-checklist",{"fr":166,"en":164,"ar":167,"es":168},"docker-compose-production-checklist","docker-compose-في-الإنتاج-قائمة-التحقق-من-10-نقاط","checklist-docker-compose-en-produccion","Docker Compose in Production: 10-Point Checklist","Docker Compose production checklist: 10 essential settings, health checks, secrets without downtime, rollback strategy, common error troubleshooting, CVE-2026-17106.",12,"2026-08-06T00:00:00+00:00","2026-09-29T14:40:45+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},[176],{"id":23,"name":24,"slug":25,"color":26,"icon":25},"\u002Fblog\u002Fcovers\u002Fdocker-compose-production-checklist-poster.svg",1790693249197]