Podman Rootless and Rootful Capabilities
`CapEff: 00000000800405fb` in a rootless container, and the identical value under sudo. Eleven capabilities either way, no CAP_SYS_ADMIN in either, and `mount` fails in both - the difference is reach, not the bitmask.
Security and Operations Guide 41 of 47 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 14 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
- guide 3 - the user namespace is what makes identical capabilities mean different things.
-
Read the mask twice
/proc/self/statusinside a container reports what it actually holds. Rootless:CapPrm: 00000000800405fb CapEff: 00000000800405fb CapBnd: 00000000800405fbNow the same thing under
sudo:CapEff: 00000000800405fbIdentical. This is the thing worth correcting: rootful Podman does not hand a container a bigger capability set. Both get the same eleven, because the default set is a property of Podman's configuration, not of who ran it.
capsh --decodeturns the hex into names:cap_chown, cap_dac_override, cap_fowner, cap_fsetid, cap_kill, cap_setgid, cap_setuid, cap_setpcap, cap_net_bind_service, cap_sys_chroot, cap_setfcapRead what is absent: no
cap_sys_admin, nocap_net_admin, nocap_net_raw, nocap_sys_module, nocap_sys_ptrace. Podman drops those by default for everyone. A container is not "root on the host minus namespaces" - it starts from a deliberately reduced set.bash Example session podman run --rm docker.io/library/alpine grep -E '^Cap(Prm|Eff|Bnd)' /proc/self/statusCapPrm: 00000000800405fbCapEff: 00000000800405fbCapBnd: 00000000800405fbsudo -n podman run --rm docker.io/library/alpine grep -E '^Cap(Prm|Eff|Bnd)' /proc/self/statusTrying to pull docker.io/library/alpine:latest...Getting image source signaturesCopying blob sha256:55afa1ecc21d2bb5e5045f32dafee56272ffd89860bac26f6c32123439af26a4Copying config sha256:d529dd0c6e5597ac7e4a3e2dea65c3fcc6173f4cae713c409265c1dd9914a11bWriting manifest to image destinationCapPrm: 00000000800405fbCapEff: 00000000800405fbCapBnd: 00000000800405fbpodman run --rm docker.io/library/alpine sh -c 'apk add -q libcap 2>/dev/null; capsh --decode=$(grep CapEff /proc/self/status | cut -f2)'0x00000000800405fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_sys_chroot,cap_setfcapExpected resultThe same three-line mask in both containers, and eleven decoded capability names.
Success conditionYou have the same capability mask from a rootless and a rootful container.
-
So what does rootless actually cost you
Three things that fail rootless, and the fourth line is the interesting one:
$ mount -t tmpfs none /mnt mount: permission denied (are you root?) $ sysctl -w kernel.hostname=nope sysctl: error setting key 'kernel.hostname': Read-only file system $ echo 1 > /proc/sys/net/ipv4/ip_forward sh: can't create /proc/sys/net/ipv4/ip_forward: Read-only file systemNow the same
mount, rootful:$ sudo podman run --rm alpine mount -t tmpfs none /mnt mount: mounting none on /mnt failed: Permission deniedIt fails there too. Not because of the user namespace - because
cap_sys_adminis not in the default set, andmountneeds it.sudodid not help, and reaching for it would not have helped.Note also the two
/proc/sysfailures say Read-only file system, not permission denied. Podman mounts/proc/sysread-only regardless of capabilities, so there is no capability to add that would let a container change a host sysctl. That is a mount option, not a privilege - a different mechanism reaching the same end.So the honest summary: the default capability set is the main constraint, and it is the same either way. What rootless additionally costs you is narrower than people assume, and it is specific: no privileged ports below 1024 (guide 51), no UIDs past your subuid range (guide 34), no macvlan (guide 54), and a userspace network stack (guide 53).
bash Example session podman run --rm docker.io/library/alpine mount -t tmpfs none /mntmount: permission denied (are you root?)[exit 1]podman run --rm docker.io/library/alpine sysctl -w kernel.hostname=nopesysctl: error setting key 'kernel.hostname': Read-only file system[exit 1]podman run --rm docker.io/library/alpine sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'sh: can't create /proc/sys/net/ipv4/ip_forward: Read-only file system[exit 1]sudo -n podman run --rm docker.io/library/alpine mount -t tmpfs none /mntmount: mounting none on /mnt failed: Permission denied[exit 255]Expected resultThree rootless failures, and the same
mountfailing rootful as well.Success conditionYou can name something
sudo podmandoes not fix. -
Where the difference does live
If the capabilities match, what is rootful actually buying? Two things, and neither is a capability:
1. The namespace those capabilities apply in. Rootless, your eleven capabilities are held in *your* user namespace, so
cap_chowncan only chown to UIDs mapped into it - which is exactly the ceiling measured in guide 34. Rootful,cap_chownmeans the whole UID space. Same capability, different reach.2. What you are allowed to ask for.
--privileged,--cap-add=SYS_ADMIN,--device, macvlan, a real bridge - a rootful Podman can grant these because root has them to give. Rootless cannot grant what it does not hold, so the flags exist and quietly do less.The practical consequence is a good habit: when something fails in a container, read whether the message names a capability or a namespace.
Operation not permittedon a syscall is usually a missing capability, and--cap-addmay fix it in either mode.Invalid argumenton a UID, orRead-only file systemon/proc/sys, is structural - no capability will help, andsudois a different machine (guide 14) rather than a bigger one.bash Example session podman run --rm --cap-drop=ALL --security-opt no-new-privileges docker.io/library/alpine iduid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)Expected result
uid=0(root)in a container holding no capabilities at all.Success conditionYou can say what rootful gives you without saying "more capabilities".
Troubleshooting
Operation not permittedon a syscall you expected to work.Why: A capability outside Podman's default eleven.
Fix:Identify it from the syscall -
mountisSYS_ADMIN, raw sockets areNET_RAW,ptraceisSYS_PTRACE- then--cap-add=. Rootless can only add what your namespace holds.--privilegeddoes not behave as documented rootless.Why: It grants everything *available*, and rootless has far less available.
Fix:Expected.
podman run --rm --privileged alpine grep CapEff /proc/self/statusshows what you actually got.pingfails inside a container.Why:
cap_net_rawis not in the default set.Fix:
--cap-add=NET_RAW, or rely onnet.ipv4.ping_group_rangewhich many distributions set permissively.You need to set a sysctl for the container.
Why:
/proc/sysis mounted read-only inside.Fix:
--sysctl net.core.somaxconn=1024on the run command - Podman sets it namespaced at creation. Only namespaced sysctls can be set this way.