CertGrid CertGrid
Concepts·Podman

Rootless Podman Defaults

You are root inside the container and uid 1000 outside it, at the same instant, for the same process. A file the container creates as root lands in your home directory owned by you - and `sudo podman images` is empty.

Foundations Guide 3 of 47 Intermediate

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.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. Two answers to the same question

    Ask who you are on the host, then ask the same thing inside a container.

    You get uid=1000(sysadmin) and uid=0(root). Both are true. Neither is a trick and nothing was escalated - sudo was not used and no setuid binary ran your container.

    bash Example session
    iduid=1000(sysadmin) gid=1000(sysadmin) groups=1000(sysadmin),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),101(lxd)podman run --rm 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 resultuid=1000(sysadmin) on the host, uid=0(root) inside the container.

    Success conditionYou have the host's answer written down before the container contradicts it.

  2. The mapping that makes both true

    /proc/self/uid_map is where the contradiction resolves. On the host it is the trivial identity map - every UID is itself:

             0          0 4294967295

    Inside the container it is two lines, and they are the entire rootless model:

             0       1000          1
             1     100000      65536

    Read each as inside, outside, count:

    • Container UID 0 is host UID 1000, for 1 UID. Root in this container is you.
    • Container UIDs 1 through 65536 are host UIDs 100000 upward. Those are the subordinate UIDs from /etc/subuid, which the kernel let you claim because an administrator allocated them to you in advance.

    So the container's root has exactly your privileges, and its other users have the privileges of accounts that do not otherwise exist. There is no path from uid 0 in here to uid 0 out there, because no line in the map points at it.

    bash Example session
    cat /proc/self/uid_map         0          0 4294967295podman run --rm docker.io/library/alpine cat /proc/self/uid_map         0       1000          1         1     100000      65536

    Expected resultThe identity map on the host, and a two-line map inside the container.

    Success conditionYou can read a uid_map line as inside, outside, count.

  3. One process, two identities

    The mapping is not a label on a container - it applies to the running process, and you can see both views of the same PID.

    podman top looks from inside: the process is root, PID 1. ps on the host looks from outside: the same process is sysadmin. Nothing translated the answer; the kernel genuinely reports a different UID depending on which namespace is asking.

    The stop is worth a glance too. sleep does not handle SIGTERM when it is PID 1, so Podman waits ten seconds and then sends SIGKILL. That is its own guide later in the path - it is not a Podman quirk, it is what PID 1 means.

    bash Example session
    podman run -d --name sleeper docker.io/library/alpine sleep 3001c580317306e487ec11dce24ca3eae5abd0d26d3065f13e6aa2660ca09cc82a3podman top sleeper user pidUSER        PIDroot        1ps -o pid,user,comm -p $(podman inspect -f '{{.State.Pid}}' sleeper)    PID USER     COMMAND   8037 sysadmin sleeppodman stop sleepertime="2026-08-22T10:56:14Z" level=warning msg="StopSignal SIGTERM failed to stop container sleeper in 10 seconds, resorting to SIGKILL"sleeperpodman rm sleepersleeper

    Expected resultroot 1 from inside, and the same process owned by sysadmin outside.

    Success conditionYou have looked at one process from both sides of the namespace.

  4. The file test

    This is the one to remember, because it is where rootless stops being trivia and starts costing you an afternoon.

    Bind mount a directory from your home, and have the container's root create a file in it. From inside, ls -ln reports owner 0. From outside, the same file reports owner 1000.

    Nothing changed the file. 0 inside is 1000 outside, so both listings are reporting the same number through different maps.

    The corollary is what bites people: a container process running as any user other than root writes files owned by a host UID in the 100000 range, which your account cannot delete without help. That failure, and the --userns=keep-id flag that avoids it, is the subject of the Storage track.

    bash Example session
    mkdir -p ~/sharedpodman run --rm -v ~/shared:/data docker.io/library/alpine sh -c 'touch /data/made-by-root && ls -ln /data'total 0-rw-r--r--    1 0        0                0 Aug 22 10:56 made-by-rootls -ln ~/sharedtotal 0-rw-r--r-- 1 1000 1000 0 Aug 22 10:56 made-by-root

    Expected resultOwner 0 inside the container, owner 1000 outside, for one file.

    Success conditionYou have produced the same file with two different owners and can say why.

  5. Rootful is a different machine

    Rootless and rootful Podman are not two modes of one installation. They are two separate stores, two separate sets of containers, and two separate networks that happen to share a binary.

    podman info reports a graphroot under your home. sudo podman info reports /var/lib/containers/storage and rootless=false.

    The proof is the image list. You pulled three images in the last two guides. sudo podman images shows none of them - not because permission was denied, but because root has never pulled anything and is looking in a different directory.

    Practical consequence: sudo is not a way to fix a Podman problem. It is a way to get a different, empty Podman. If a command fails without sudo, adding it will usually make the error go away by making the whole context go away.

    bash Example session
    podman info --format 'graphroot={{.Store.GraphRoot}} rootless={{.Host.Security.Rootless}}'graphroot=/home/sysadmin/.local/share/containers/storage rootless=truesudo -n podman info --format 'graphroot={{.Store.GraphRoot}} rootless={{.Host.Security.Rootless}}'graphroot=/var/lib/containers/storage rootless=falsepodman imagesREPOSITORY                     TAG         IMAGE ID      CREATED       SIZEdocker.io/library/nginx        alpine      7bc5ba2f958a  2 days ago    64.2 MBdocker.io/library/alpine       latest      d529dd0c6e55  2 months ago  8.71 MBdocker.io/library/hello-world  latest      e2ac70e7319a  5 months ago  25.5 kBsudo -n podman imagesREPOSITORY  TAG         IMAGE ID    CREATED     SIZE

    Expected resultTwo different graphroots, rootless=true then false, and an empty image list for root.

    Success conditionYou can explain why sudo podman images is empty on a machine full of images.

Troubleshooting

Official sources