CertGrid CertGrid
Concepts·Podman

Rootless Podman Image Storage

185 MB of images in `~/.local/share/containers/storage`, on your home filesystem and counted against your quota. The same layer directories read `1000 1000` from outside and `0 0` under `podman unshare`.

Containers and Images Guide 9 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 directories, not one

    Podman keeps persistent data and runtime data in different places, and the distinction matters when something goes wrong:

    • graphroot - ~/.local/share/containers/storage. Images, layers, volumes. Survives reboots.
    • runroot - /run/user/1000/containers. Locks, network state, the running container's scratch. On tmpfs, so a reboot clears it.

    185 MB of images here, and du is reading your home filesystem. That is the first practical consequence: images count against your home directory's quota, and on a machine where /home is a small separate partition you will fill it long before the root filesystem notices.

    podman system df gives the engine's own accounting, which is the number to quote when deciding what to prune.

    bash Example session
    podman info --format 'graphroot={{.Store.GraphRoot}} runroot={{.Store.RunRoot}} driver={{.Store.GraphDriverName}}'graphroot=/home/sysadmin/.local/share/containers/storage runroot=/run/user/1000/containers driver=overlaydu -sh ~/.local/share/containers/storage185M	/home/sysadmin/.local/share/containers/storagels ~/.local/share/containers/storagedb.sqldefaultNetworkBackendlibpodnetworksoverlayoverlay-containersoverlay-imagesoverlay-layerssecretsstorage.lockuserns.lockvolumespodman system dfTYPE           TOTAL       ACTIVE      SIZE        RECLAIMABLEImages         5           0           188.4MB     188.4MB (100%)Containers     0           0           0B          0B (0%)Local Volumes  0           0           0B          0B (0%)

    Expected resultA graphroot under your home, a runroot under /run/user, and roughly 185 MB across twelve entries.

    Success conditionYou can say which of the two directories survives a reboot.

  2. What the layout means

    The directory names are the storage driver: overlay, overlay-images, overlay-layers, overlay-containers. Change driver and you get a different set - which is why switching drivers appears to lose all your images.

    overlay-images/ contains one directory per image, named by image ID, plus images.json - the index that maps names and tags onto those IDs. A tag is a line in that file, which is why tagging is instant and costs no disk.

    db.sql is Podman's own state database. Since there is no daemon holding state in memory, it has to be on disk - and storage.lock is how concurrent podman commands avoid corrupting it.

    findmnt confirms there is nothing special underneath: plain ext4 on the root logical volume. No dedicated filesystem, no thin pool, no device mapper.

    bash Example session
    ls ~/.local/share/containers/storage/overlay-images/00c30ddf0ef8074bbc7b7e5ea655bb6d359dc66694edd57d70fe95ce6ba531aa7bc5ba2f958a043e123135f456af857350673b64eaddcf811698239f3a53d6e6c6348fa86ba0fb2108c9334f5fe913ddc6d853313e655891f133a0127c30099fd529dd0c6e5597ac7e4a3e2dea65c3fcc6173f4cae713c409265c1dd9914a11be2ac70e7319a02c5a477f5825259bd118b94e8b02c279c67afa63adab6d8685bimages.jsonimages.lockpodman image inspect docker.io/library/alpine --format 'driver={{.GraphDriver.Name}} layers={{len .RootFS.Layers}}'driver=overlay layers=1findmnt -T ~/.local/share/containers/storage -o TARGET,SOURCE,FSTYPETARGET SOURCE                            FSTYPE/      /dev/mapper/ubuntu--vg-ubuntu--lv ext4

    Expected resultFive image directories plus an index, a single-layer alpine, and ext4.

    Success conditionYou can find the on-disk directory for a given image ID.

  3. The same files, two owners

    This is the parallel to the file test in guide 3, and it is worth seeing on the store itself.

    ls -ln on the layer directories reports 1000 1000 - you own them, which is what makes rootless possible at all.

    The same ls under podman unshare reports 0 0. Same directories, same bytes, and root owns them - because inside the user namespace your UID 1000 *is* 0.

    podman unshare is the practical tool that falls out of this. When a file in the store or a volume is owned by a UID your account cannot touch, running the command inside the namespace puts you back in the world where you own it. It is the correct answer to "permission denied on a file I created", and it is better than sudo, which would move you to a different store entirely.

    bash Example session
    ls -ln ~/.local/share/containers/storage/overlay | head -4total 76drwx------ 5 1000 1000 4096 Aug 22 11:08 03430695163ab64f23824b1643197bd4f0762c2956f4ab5ac8bc33b7e9ca082cdrwx------ 6 1000 1000 4096 Aug 22 11:30 0958e0fef2d6a31e1325b8bfecd99dead933363682d69850a7606599023751bcdrwx------ 5 1000 1000 4096 Aug 22 11:08 0994c120661f7fe9296a272f0d48fdc6ea6c20341d91af32bb86d3064317d69fpodman unshare ls -ln ~/.local/share/containers/storage/overlay | head -4total 76drwx------ 5 0 0 4096 Aug 22 11:08 03430695163ab64f23824b1643197bd4f0762c2956f4ab5ac8bc33b7e9ca082cdrwx------ 6 0 0 4096 Aug 22 11:30 0958e0fef2d6a31e1325b8bfecd99dead933363682d69850a7606599023751bcdrwx------ 5 0 0 4096 Aug 22 11:08 0994c120661f7fe9296a272f0d48fdc6ea6c20341d91af32bb86d3064317d69fstat -c 'owner_uid=%u %n' ~/.local/share/containers/storage/overlayowner_uid=1000 /home/sysadmin/.local/share/containers/storage/overlay

    Expected result1000 1000 from outside and 0 0 from inside, for the same directories.

    Success conditionYou have read one directory's ownership two different ways and can explain both.

  4. Root's store is a different, empty machine

    sudo podman info reports /var/lib/containers/storage, and it holds 160 KB and no images at all - on a host with 185 MB of them.

    Root has never pulled anything. There is no sharing, no fallback and no promotion between the two stores.

    Two practical consequences:

    • Disk is counted twice. Pull the same image rootless and rootful and it occupies space in both trees. On a machine where several users run containers, each has a full copy of every image they use.
    • sudo is not a fix. Reaching for it when a rootless command fails gets you a working Podman with none of your images, which usually looks like a new and more confusing problem.

    If you genuinely need one shared image store, that is what an additional store configured read-only in storage.conf is for - and it is a deliberate setup, not a default.

    bash Example session
    sudo -n podman info --format 'graphroot={{.Store.GraphRoot}}'graphroot=/var/lib/containers/storagesudo -n du -sh /var/lib/containers/storage160K	/var/lib/containers/storagesudo -n podman imagesREPOSITORY  TAG         IMAGE ID    CREATED     SIZE

    Expected result/var/lib/containers/storage, 160 KB, and an empty image list.

    Success conditionYou can explain why the same image can occupy disk twice on one host.

Troubleshooting

Official sources