Podman Volume Ownership and Permissions
A container running as its own user 1000 writes files owned by host UID 100999. A single file you can still delete; a directory tree you cannot - `rm -rf` fails with Permission denied and `podman unshare rm -rf` works.
Storage Guide 33 of 47 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 15 min
- Reviewed22 August 2026
Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
-
A non-root container user, and where its files land
--user 1000makes the container's process uid 1000 inside.idconfirmsuid=1000(sysadmin) gid=0(root), and the write succeeds.Worth saying plainly: the write is not denied. That is what this guide was planned around and it is wrong - the directory is group-writable and the container's gid 0 maps to your own gid 1000, so the process has permission.
The problem is the ownership. Compare the two views of the same directory:
inside -rw-r--r-- 1 1000 0 by-container-user.txt outside -rw-r--r-- 1 100999 1000 by-container-user.txt100999. Not 1000. Read it off
/etc/subuid, which sayssysadmin:100000:65536: container uid 1 is host uid 100000, so container uid 1000 is host uid 100999.Notice that
by-container-root.txtfrom the previous guide is still owned by 1000. Container root maps to you; every other container user maps into the subordinate range. That is the whole asymmetry.bash Example session podman run --rm --user 1000 -v ~/src:/src docker.io/library/alpine sh -c 'id; echo hi > /src/by-container-user.txt; ls -ln /src'uid=1000(sysadmin) gid=0(root) groups=0(root)total 12-rw-r--r-- 1 0 0 3 Aug 22 12:10 by-container-root.txt-rw-r--r-- 1 1000 0 3 Aug 22 12:10 by-container-user.txt-rw-rw-r-- 1 0 0 14 Aug 22 12:10 host.txtrm -rf ~/src && mkdir -p ~/src && echo "from the host" > ~/src/host.txt && ls -ln ~/srctotal 4-rw-rw-r-- 1 1000 1000 14 Aug 22 12:10 host.txtcat /etc/subuidsysadmin:100000:65536Expected result
uid=1000inside,100999outside, and a subuid base of 100000.Success conditionYou can compute the host UID a container user will write as.
-
One file is fine. A directory is not.
The single file can be removed:
$ rm -f ~/src/by-container-user.txt # succeedsWhich surprises people who expect ownership to decide. It does not - POSIX unlink permission comes from write access to the containing directory, and you own that. The file's owner is irrelevant.
Now have the container create a tree, which is what a real application does:
$ stat -c 'dir owner uid=%u %n' ~/src/tree ~/src/tree/deep dir owner uid=100999 /home/sysadmin/src/tree dir owner uid=100999 /home/sysadmin/src/tree/deep $ rm -rf ~/src/tree rm: cannot remove '/home/sysadmin/src/tree/deep/f.txt': Permission denied $ touch ~/src/tree/newfile touch: cannot touch ...: Permission deniedNow the directories are owned by 100999, so you have no write access inside them - you can neither delete their contents nor add to them.
rm -rffails part-way through and leaves a partial tree.This is the shape of the real complaint: a build container wrote
node_modulesor atarget/directory, and your own account cannot clean it up.bash Example session podman run --rm --user 1000 -v ~/src:/src docker.io/library/alpine sh -c 'mkdir -p /src/tree/deep && echo x > /src/tree/deep/f.txt; ls -ln /src/tree'total 4drwxr-xr-x 2 1000 0 4096 Aug 22 12:10 deepls -ln ~/src/treetotal 4drwxr-xr-x 2 100999 1000 4096 Aug 22 12:10 deepstat -c 'dir owner uid=%u %n' ~/src/tree ~/src/tree/deepdir owner uid=100999 /home/sysadmin/src/treedir owner uid=100999 /home/sysadmin/src/tree/deeprm -rf ~/src/treerm: cannot remove '/home/sysadmin/src/tree/deep/f.txt': Permission denied[exit 1]touch ~/src/tree/newfiletouch: cannot touch '/home/sysadmin/src/tree/newfile': Permission denied[exit 1]Expected resultDirectories owned by 100999, and Permission denied from both
rm -rfandtouch.Success conditionYou have a directory on your own machine that you cannot delete.
-
podman unshare is the way out
$ podman unshare rm -rf ~/src/treeSilent success.
podman unshareruns a command inside your user namespace, where host 100999 appears as an ordinary uid you have authority over - the same namespace whoseuid_mapyou read in guide 3.This is the correct tool and it is worth learning instead of the alternatives:
sudo rm -rfworks, but reaching for root to clean up after an unprivileged container is the wrong instinct, and on a shared host you may not have it.chown -Rneeds root for the same reason.
podman unshareneeds neither. Anything you can run, you can run in there -podman unshare ls -ln,podman unshare chown -R 0:0 ., a whole shell withpodman unshare bash.bash Example session podman unshare rm -rf ~/src/treels ~/srcby-container-root.txthost.txtExpected resultNo output, and the tree gone.
Success conditionYou removed files your own account could not touch, without root.
-
Prevent it with --userns=keep-id
Better than cleaning up is not creating the problem.
--userns=keep-idmaps your uid to the same number inside instead of mapping container root to you:$ podman run --rm --userns=keep-id ... id uid=1000(sysadmin) gid=1000(sysadmin) groups=1000(sysadmin)Both uid and gid, unlike the
--user 1000case where gid was 0. And the file it writes is owned by1000 1000on both sides - the inside and outside listings agree for the first time in this guide.When to use which:
--userns=keep-idwhenever a container writes to a bind mount of yours. Development containers, build containers, anything touching your source tree. This should be a habit.- The default for containers writing only to named volumes, where nothing on the host reads the files by hand.
--userns=keep-id:uid=1000,gid=1000when the image expects a specific non-root uid and you need to line it up with yours.
The trade-off: with
keep-idthe container has no root at all, so an image that wants toapt-get installat startup will fail. That is usually a sign the image should have done it at build time.bash Example session podman run --rm --userns=keep-id -v ~/src:/src docker.io/library/alpine sh -c 'id; echo hi > /src/with-keep-id.txt; ls -ln /src'uid=1000(sysadmin) gid=1000(sysadmin) groups=1000(sysadmin)total 12-rw-r--r-- 1 1000 1000 3 Aug 22 12:10 by-container-root.txt-rw-rw-r-- 1 1000 1000 14 Aug 22 12:10 host.txt-rw-r--r-- 1 1000 1000 3 Aug 22 12:10 with-keep-id.txtrm -rf ~/src && mkdir -p ~/src && echo "from the host" > ~/src/host.txt && ls -ln ~/srctotal 4-rw-rw-r-- 1 1000 1000 14 Aug 22 12:10 host.txtExpected result
uid=1000 gid=1000inside, and a new file owned by 1000 on the host.Success conditionA container wrote a file you can delete without help.
Troubleshooting
rm -rffails part-way with Permission denied on a directory a container created.Why: The directories are owned by a subordinate uid.
Fix:
podman unshare rm -rf <path>.A container cannot read files you own, even with
--userns=keep-id.Why:
keep-idmaps your uid, but the process inside may still be running as a different user from the image'sUSERline.Fix:Add
--user $(id -u):$(id -g)alongside, or--userns=keep-id:uid=<n>,gid=<n>to line the numbers up.there might not be enough IDs available in the namespacewithkeep-id.Why: Mapping your uid to itself needs the range to span it, and a small subuid allocation may not.
Fix:Widen the range - see guide 34, which is the same arithmetic.
Files are owned by 100999 and you want them owned by you, in place.
Why: They were written by a non-root container user.
Fix:
podman unshare chown -R 0:0 <path>- inside the namespace, uid 0 is you, so this makes them yours outside.