[The first part is written by me, then an AI-written report follows, which I deem correct]
Apparently containers/storage expects that containers are stored with 0-based UIDs on disk, but also fails to idmap the upper layers so that this actually happens, and even fails to record the fact in the layer.
The result is that images created by podman save have UID values in the uidmap range, containers cannot start on those images, and ALL building with buildah with userns enabled is broken.
In general, this shows complete carelessness in implementation and testing. Userns with mount idmapping is the only code path that provides both security and performance, and needs to work and be the main tested and used path.
Here is what I think probably needs to be fixed:
- Hardcoding storage to have 0-based UIDs is completely absurd. Instead, it should be allowed to be in ANY UID range, and then idmapped back using the composition of the storage layer idmap and the current container idmap (e.g. if it's stored as UID 200000 based and the current container uses UID 300000 based, idmap UID 200000 to 300000 in the mount idmap - or perhaps map UID 200000 to 0 or some fixed high range in the idmap for that layer, and then map 0 or some fixed high range to 300000 into an idmap for the whole overlayfs stack). This is needed since files on disk must reflect the container who created them for security reasons. When pulling images, it should use a fixed high UID range used for this purpose.
- The UID map needs to be always stored. Currently it seems that if shifting is supported, then the UID map is not even stored.
It's pretty unbelievable that an issue of this magnitude exists.
Using podman 5.8.2.
[text removed as CoC violation]
Claude Fable 5 report
Committing a container that uses an ID mapping produces image layers with host-shifted UIDs/GIDs when idmapped mounts are in use
Description
When the overlay driver uses idmapped mounts (SupportsShifting() true: rootful, native overlay, no mount_program, recent kernel), committing a mapped container emits host-shifted UIDs/GIDs into the image layer instead of canonical 0-based ones.
Consequences:
podman run --userns=auto <committed-image> fails: the container needs a user namespace with size <huge> that is bigger than the maximum value allowed with userns=auto 65536.
- With a fixed mapping, files from the committed layer appear as
nobody:nogroup in the next container (the idmapped lower mount shifts already-shifted IDs out of range).
podman build / buildah build --userns-uid-map breaks at every step boundary, since each step commits an intermediate image that the next step consumes as an idmapped lower.
- Host subordinate-ID layout leaks into pushable OCI blobs.
Reproducible with plain podman run + podman commit, so the defect is in containers/storage; podman and buildah are affected consumers.
Steps to reproduce
# podman run --name t --userns=auto alpine touch /x
# podman commit t t2
# podman run --rm --userns=auto t2 ls -ln /x
Error: creating container storage: the container needs a user namespace with
size 1074136065 that is bigger than the maximum value allowed with userns=auto 65536
Top layer tar of podman save t2 (everything written through the mapped container carries the auto-allocated host ID; untouched base content is correctly 0-based):
-rw-r--r-- 1074136064/1074136064 0 2026-06-11 20:15 x
-rwxr-xr-x 1074136064/1074136064 0 2026-06-11 20:15 run/.containerenv
drwxr-xr-x 0/0 0 2026-04-15 04:51 bin/
Expected results
Committed layers contain 0-based IDs, as they do when idmapped mounts are unavailable.
Root cause (from current main)
store.CreateContainer: when canUseShifting(), the container's layer is created with HostUIDMapping: true, UIDMap: nil — the real map is kept only on the container record, not the layer.
overlay.get(): idmapped mounts are applied only to lowers; upperdir is the raw diff. All writes (RUN output through the userns; buildah COPY, which chowns ToHost) land physically shifted in the diff.
layerStore.Diff untranslates via layerMappings(toLayer) — nil because of (1) — so the diff tar keeps the shifted IDs and the committed layer is shifted on disk while recorded as unmapped.
- Next use: the layer is accepted as-is (
layerMatchesMappingOptions: shifting + no maps) and mounted with an idmapped mount from a 0-based source → double shift → overflow ID; --userns=auto sizing reads the same shifted IDs.
Writers still follow the legacy chown-scheme contract (shifted on disk, unshift at Diff via recorded maps); the shifting scheme blanks those maps and assumes 0-based content. The unshift silently no-ops.
Workaround (untested, per code)
_CONTAINERS_OVERLAY_DISABLE_IDMAP=yes should force the legacy chown path, where layer maps are recorded and Diff untranslates.
Possible fix
Record the true maps on the container's layer even under shifting — the upper is never idmapped at mount time, so this is truthful and lets Diff untranslate, without changing the 0-based scheme for shared image lowers.
Prior sighting
podman-container-tools/podman#16795 (2022): same signature (podman build --userns=auto, step 2 demands userns size = step 1's auto base + offset); closed as a subuid-range/size configuration problem. Raising auto-userns-max-size only masks it — files get wrong high in-container UIDs and the shift compounds per step.
Versions
$ podman --version
podman version 5.8.2
[The first part is written by me, then an AI-written report follows, which I deem correct]
Apparently containers/storage expects that containers are stored with 0-based UIDs on disk, but also fails to idmap the upper layers so that this actually happens, and even fails to record the fact in the layer.
The result is that images created by podman save have UID values in the uidmap range, containers cannot start on those images, and ALL building with buildah with userns enabled is broken.
In general, this shows complete carelessness in implementation and testing. Userns with mount idmapping is the only code path that provides both security and performance, and needs to work and be the main tested and used path.
Here is what I think probably needs to be fixed:
It's pretty unbelievable that an issue of this magnitude exists.
Using podman 5.8.2.
[text removed as CoC violation]
Claude Fable 5 report
Committing a container that uses an ID mapping produces image layers with host-shifted UIDs/GIDs when idmapped mounts are in use
Description
When the overlay driver uses idmapped mounts (
SupportsShifting()true: rootful, native overlay, nomount_program, recent kernel), committing a mapped container emits host-shifted UIDs/GIDs into the image layer instead of canonical 0-based ones.Consequences:
podman run --userns=auto <committed-image>fails:the container needs a user namespace with size <huge> that is bigger than the maximum value allowed with userns=auto 65536.nobody:nogroupin the next container (the idmapped lower mount shifts already-shifted IDs out of range).podman build/buildah build --userns-uid-mapbreaks at every step boundary, since each step commits an intermediate image that the next step consumes as an idmapped lower.Reproducible with plain
podman run+podman commit, so the defect is in containers/storage; podman and buildah are affected consumers.Steps to reproduce
Top layer tar of
podman save t2(everything written through the mapped container carries the auto-allocated host ID; untouched base content is correctly 0-based):Expected results
Committed layers contain 0-based IDs, as they do when idmapped mounts are unavailable.
Root cause (from current main)
store.CreateContainer: whencanUseShifting(), the container's layer is created withHostUIDMapping: true, UIDMap: nil— the real map is kept only on the container record, not the layer.overlay.get(): idmapped mounts are applied only to lowers;upperdiris the raw diff. All writes (RUN output through the userns; buildah COPY, which chownsToHost) land physically shifted in the diff.layerStore.Diffuntranslates vialayerMappings(toLayer)— nil because of (1) — so the diff tar keeps the shifted IDs and the committed layer is shifted on disk while recorded as unmapped.layerMatchesMappingOptions: shifting + no maps) and mounted with an idmapped mount from a 0-based source → double shift → overflow ID;--userns=autosizing reads the same shifted IDs.Writers still follow the legacy chown-scheme contract (shifted on disk, unshift at Diff via recorded maps); the shifting scheme blanks those maps and assumes 0-based content. The unshift silently no-ops.
Workaround (untested, per code)
_CONTAINERS_OVERLAY_DISABLE_IDMAP=yesshould force the legacy chown path, where layer maps are recorded and Diff untranslates.Possible fix
Record the true maps on the container's layer even under shifting — the upper is never idmapped at mount time, so this is truthful and lets Diff untranslate, without changing the 0-based scheme for shared image lowers.
Prior sighting
podman-container-tools/podman#16795 (2022): same signature (
podman build --userns=auto, step 2 demands userns size = step 1's auto base + offset); closed as a subuid-range/size configuration problem. Raisingauto-userns-max-sizeonly masks it — files get wrong high in-container UIDs and the shift compounds per step.Versions