01
Capture the operation that is actually denied
Read, append, create, and rename are different filesystem operations. An application may read a configuration file successfully but fail to create a new output file in the same directory. Capture the full path, operation, and error rather than reducing all storage failures to a Docker permission problem.
Check the mount mode as well. A read-only mount can reject writes even when the file owner is correct. Conversely, changing a mount to read-write cannot give an unrelated Linux UID access to a file with restrictive ownership.
02
Inspect identity and numeric ownership
Run diagnostics as the service user, not only from an interactive root shell. The numeric IDs matter even when a friendly username appears identical across two images. If the application drops privileges after startup, inspect the actual worker identity rather than assuming the image default tells the entire story.
The following commands assume a service called api and a Debian-like image with id and stat. Adapt the executable names for a minimal or distroless image; absence of a diagnostic shell is not evidence that the path is writable.
docker compose exec api id
docker compose exec api stat -c '%u:%g %a %n' /data /data/result.txt
container_id=$(docker compose ps --all --quiet api)
docker inspect --format '{{json .Mounts}}' "$container_id"03
Reproduce an append failure without touching project data
Create a new volume specifically for this example. The first container writes a file as root with mode 640. The second container attempts an append as UID/GID 10001:10001 and should fail. The volume name returned by Docker is used directly, so an existing application volume is not selected accidentally.
Our validation reproduced the denied append. This is stronger evidence than comparing usernames: it tests the operation under the intended numeric identity against the same stored file.
demo_volume=$(docker volume create)
docker run --rm --mount "type=volume,src=$demo_volume,dst=/data" python:3.12-slim sh -c 'printf "original\n" > /data/result.txt; chmod 640 /data/result.txt'
# Expected to fail: this UID does not own the file.
docker run --rm --user 10001:10001 --mount "type=volume,src=$demo_volume,dst=/data" python:3.12-slim sh -c 'printf "new\n" >> /data/result.txt'04
Repair the specific ownership relationship
For the disposable example, change ownership of just the result file. Keep mode 640. The non-root process should now append successfully while the original content remains. This does not require running the application permanently as root or setting every file to mode 777.
For an actual service, choose a stable runtime identity and arrange only its necessary writable paths. A Dockerfile ownership setting applies to image files; an already populated runtime volume has its own stored ownership. Do not recursively chown a shared database or a host project merely because one append failed.
docker run --rm --mount "type=volume,src=$demo_volume,dst=/data" python:3.12-slim chown 10001:10001 /data/result.txt
docker run --rm --user 10001:10001 --mount "type=volume,src=$demo_volume,dst=/data" python:3.12-slim sh -c 'printf "new\n" >> /data/result.txt; stat -c "%u:%g %a" /data/result.txt; cat /data/result.txt'
# Remove only this example's new volume after reviewing the result.
docker volume rm "$demo_volume"05
Use a separate branch when ownership already matches
If the numeric owner, parent directory permissions, and mount mode are appropriate but access still fails, inspect ACLs, security labels, user-namespace mappings, and the storage driver. A namespaced root user can map to an unprivileged host UID; copying host ownership assumptions into that environment can be wrong.
Close the investigation with the application operation itself: create or append the expected output as the service user and read it back. A successful root-shell touch command is not the acceptance test for a non-root service.
Sınırları bil
The reproduction uses an isolated Linux named volume, UID/GID 10001:10001, and one file. It does not validate host ACLs, SELinux labels, NFS root-squash, or rootless/user-namespace ID mappings. Docker Desktop bind mounts can behave differently from Linux-managed volumes. Reproduction checked on 26 October 2026 using Linux containers on Docker Engine 29.6.2 and Compose 5.3.1; this is the validation environment, not a minimum-version claim.
Kaynaklar ve ileri okuma
CoreTrailKit tarafından hazırlanan teknik yayın.
Bütün yazıları keşfet