`podman unshare`: Inspect UID Maps and Fix Rootless File Ownership

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Any Linux host with rootless Podman
Privilege Rootless examples as podtest; rootful error demo with sudo podman
Scope The podman unshare command — user namespace entry, uid_map inspection, host vs namespace file ownership, podman unshare chown, rootless podman mount, stopped-container filesystem edits, --rootless-netns, exit codes, and rootful failure. Does not cover subuid provisioning, full --userns modes, SELinux diagnosis, or the complete volume permission tree.
Related guides Podman volumes

Rootless Podman maps container UIDs through a user namespace. A file that looks like 984038 in your normal shell can look like 999 inside Podman's namespace — same inode, different numeric view. podman unshare drops you into that namespace so you can inspect mappings, fix ownership with container-visible IDs, and reach rootless mount points your host shell cannot see.

This guide focuses on the command itself. For the full permission diagnostic tree, start with Fix Podman volume permission denied. For --userns mode theory, see Podman user namespaces.

The lab user is podtest for every rootless example.


What does podman unshare do?

Run a command inside Podman's rootless user namespace:

bash
podman unshare id
output
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:container_runtime_t:s0-s0:c0.c1023

That uid=0(root) is root inside the rootless user's user namespace — not host UID 0. Your normal shell still runs as podtest; podman unshare is a separate view:

text
normal host shell
UID 1014
podman unshare
UID 0 inside rootless namespace
    ├── maps back to host UID 1014
    └── subordinate IDs map additional container users

podman unshare executes one command (or starts a shell) with the same ID mapping rootless containers use. These examples use Podman's normal rootless mapping. Containers created with explicit --userns modes can have additional mapping behavior; see the user-namespace guide for those cases.


Inspect UID and GID maps

Each /proc/self/uid_map line has three columns: namespace start, host start, length.

Read the mapping inside unshare:

bash
podman unshare cat /proc/self/uid_map
output
0       1014          1
         1     983040      65536

GID mapping uses the same layout:

bash
podman unshare cat /proc/self/gid_map
output
0       1015          1
         1     983040      65536

On this host, namespace UID 0 maps to host 1014, and namespace UIDs 165536 draw from the subordinate pool starting at 983040. Those pools come from /etc/subuid and /etc/subgid:

bash
grep '^podtest:' /etc/subuid /etc/subgid
output
/etc/subuid:podtest:983040:65536
/etc/subgid:podtest:983040:65536

Provisioning subordinate ranges belongs in Rootless Podman. Here you only need to read the maps unshare exposes.


See file ownership as Podman sees it

Create a file through a container running as UID 999, then compare host and namespace views.

Prepare a writable directory and create the file from a container:

bash
mkdir -p ~/unshare-demo/data && chmod 777 ~/unshare-demo/data

Create the file as container UID 999 through a bind mount:

bash
podman run --rm --user 999 -v ~/unshare-demo/data:/data:Z docker.io/library/alpine:3.20 touch /data/example

From your normal shell the owner is a subordinate host UID:

bash
ls -ln ~/unshare-demo/data/
output
total 0
-rw-r--r--. 1 984038 1015 0 Aug 23 01:07 example

Inside unshare, the same inode shows container UID 999:

bash
podman unshare ls -ln ~/unshare-demo/data/
output
total 0
-rw-r--r--. 1 999 0 0 Aug 23 01:07 example

Both listings describe the same file. The host kernel stores the mapped host ID (984038 = 983040 + 998); podman unshare shows the ID the container namespace uses. That difference is why permission fixes need namespace-aware tools.


Fix rootless ownership with podman unshare chown

You could compute subuid start + container UID by hand. podman unshare chown is simpler — you pass the container-visible ID and Podman applies the mapping.

Set ownership using container UID 999:

bash
podman unshare chown 999:999 ~/unshare-demo/data/example

Inside the namespace the owner now matches what a UID 999 process expects:

bash
podman unshare ls -ln ~/unshare-demo/data/
output
total 0
-rw-r--r--. 1 999 999 0 Aug 23 01:07 example

On the host the kernel still stores subordinate numbers — both owner and group shifted together:

bash
ls -ln ~/unshare-demo/data/
output
total 0
-rw-r--r--. 1 984038 984038 0 Aug 23 01:07 example

The metadata on disk changed. This is not a virtual view — chown through unshare updates real host ownership using Podman's namespace translation.


Recursively fix a directory

Apply the same pattern to a tree when every file should belong to container UID 999:

bash
podman unshare chown -R 999:999 ~/unshare-demo/data/

Confirm every path now shows UID 999 inside the namespace:

bash
podman unshare ls -lnR ~/unshare-demo/data/
output
/home/podtest/unshare-demo/data/:
total 0
-rw-r--r--. 1 999 999 0 Aug 23 01:07 example
drwxr-xr-x. 2 999 999 6 Aug 23 01:07 sub

/home/podtest/unshare-demo/data/sub:
total 0

Recursive chown rewrites host filesystem metadata for every matched path. On large or shared trees that can be slow and disruptive — prefer targeted chown on the paths you know are wrong. When you are unsure which layer blocks access, diagnose first in Fix Podman volume permission denied instead of blanket chown -R on /home or application data.


podman unshare chown vs :U

Both change real host ownership — neither is a display-only trick.

podman unshare chown :U mount option
explicit administrative action automatic adjustment at mount time
you choose exact target IDs derives ownership from the container user
easy to inspect before and after with unshare ls convenient but can recursively modify the source tree
change persists after the command change persists after the container stops

Use unshare chown when you need a specific container UID/GID and want to verify the result in both namespaces. Use :U when mount-time adjustment is acceptable. SELinux and mode-bit diagnosis still belong in the volume permissions guide.


Inspect a rootless container filesystem with podman mount

podman mount exposes a container's merged root filesystem on disk. Rootless Podman requires the mount namespace that unshare provides.

Create a stopped container for the demo:

bash
podman create --name unshare-mount-demo docker.io/library/alpine:3.20 sleep 3600

From a normal shell, rootless podman mount refuses to run:

bash
podman mount unshare-mount-demo
output
Error: cannot run command "podman mount" in rootless mode, must execute `podman unshare` first

The mount must happen inside the rootless namespace.


Use podman mount inside podman unshare

Run mount and list in one unshare invocation so the mount namespace stays valid:

bash
podman unshare sh -c 'mnt=$(podman mount unshare-mount-demo); echo "MOUNT=$mnt"; ls -la "$mnt/etc/hostname"'
output
MOUNT=/home/podtest/.local/share/containers/storage/overlay/025c855d0d69e01142dc3e1e6e0a0336a21472ac9a068baa833946adc46cdd2e/merged
-rw-r--r--. 1 root root 10 May  6  2024 /home/podtest/.local/share/containers/storage/overlay/025c855d0d69e01142dc3e1e6e0a0336a21472ac9a068baa833946adc46cdd2e/merged/etc/hostname

You can also start an interactive shell with podman unshare and run podman mount CONTAINER from inside it. Either way, inspect files in the same session — do not copy the path to a normal shell and expect it to work.


Why rootless podman mount is invisible outside the namespace

Rootless Podman cannot attach overlay mounts into the host's initial mount namespace with host-root privileges. The merged path exists only inside Podman's user/mount namespace:

text
host shell namespace
podman unshare mount namespace

Capture the path from unshare, then try the same path from your login shell:

bash
podman unshare sh -c 'podman mount unshare-mount-demo'
output
/home/podtest/.local/share/containers/storage/overlay/025c855d0d69e01142dc3e1e6e0a0336a21472ac9a068baa833946adc46cdd2e/merged

The same path string from your login shell cannot see the mount:

bash
ls /home/podtest/.local/share/containers/storage/overlay/025c855d0d69e01142dc3e1e6e0a0336a21472ac9a068baa833946adc46cdd2e/merged/etc/hostname
output
ls: cannot access '/home/podtest/.local/share/containers/storage/overlay/025c855d0d69e01142dc3e1e6e0a0336a21472ac9a068baa833946adc46cdd2e/merged/etc/hostname': No such file or directory

The string path is real; the mount is not visible outside unshare. That is expected rootless behavior, not a broken path.


Modify a stopped container's filesystem

A practical pattern: mount, edit the writable layer, unmount, then start the container.

bash
podman unshare sh -c 'mnt=$(podman mount unshare-mount-demo); echo modified > "$mnt/tmp/unshare-test"; podman unmount unshare-mount-demo'

Start the container and read the file from inside:

bash
podman start unshare-mount-demo

Read the file from inside the running container:

bash
podman exec unshare-mount-demo cat /tmp/unshare-test
output
modified

You changed the container's writable layer — not the image itself. Useful for forensic inspection, repairing config, or retrieving data when a container will not start. Do not treat this as a routine way to patch production images.

Clean up when finished:

bash
podman rm -f unshare-mount-demo

Unmount the container filesystem

Release the overlay mount when you no longer need the path:

bash
podman unshare podman unmount CONTAINER

Or run podman unmount CONTAINER from inside an unshare shell. Leaving mounts attached serves no purpose and can confuse later maintenance. After unmount, podman mount CONTAINER must be run again before the merged path is available.


Enter the rootless network namespace

On Podman 5.8.2, --rootless-netns joins the rootless network namespace Netavark uses for bridge networking.

List addresses from that namespace:

bash
podman unshare --rootless-netns ip -br addr
output
lo               UNKNOWN        127.0.0.1/8 ::1/128 
enp0s3           UNKNOWN        10.0.2.15/24 fd17:625c:f037:2:a00:27ff:fe2d:cd83/64 fe80::ccd3:f6ff:fe89:e46a/64 
podman1          UP             10.89.0.1/24 fe80::b4f2:1bff:fe4e:761a/64 
veth0@if2        UP             fe80::744e:d3ff:fe73:b7ce/64

Routing table from the same namespace:

bash
podman unshare --rootless-netns ip route
output
default via 10.0.2.2 dev enp0s3 proto dhcp metric 116 
10.0.2.0/24 dev enp0s3 proto kernel scope link metric 116 
10.89.0.0/24 dev podman1 proto kernel scope link src 10.89.0.1

Use this to inspect podman1, veth pairs, and bridge routes without guessing from the host namespace. Full pasta and bridge behavior lives in Rootless Podman networking.


Reach a rootless bridge container by IP

Create a user bridge network first:

bash
podman network create unshare-bridge

Start a long-lived container on that bridge so you can read its IP:

bash
podman run -d --name netns-demo --network unshare-bridge docker.io/library/alpine:3.20 sleep 600

The inspect template prints the address Podman assigned:

bash
podman inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' netns-demo
output
10.89.0.2

From the host's normal namespace, ping often fails even though the container is running:

bash
ping -c1 -W1 10.89.0.2
output
PING 10.89.0.2 (10.89.0.2) 56(84) bytes of data.

--- 10.89.0.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

The same address responds inside --rootless-netns:

bash
podman unshare --rootless-netns ping -c1 -W2 10.89.0.2
output
PING 10.89.0.2 (10.89.0.2) 56(84) bytes of data.
64 bytes from 10.89.0.2: icmp_seq=1 ttl=64 time=0.192 ms

--- 10.89.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.192/0.192/0.192/0.000 ms

That is when --rootless-netns earns its place — debugging connectivity to bridge IPs the host namespace cannot reach directly.

Remove the demo container and network:

bash
podman rm -f netns-demo

Drop the bridge network once the container is gone:

bash
podman network rm unshare-bridge

podman unshare is rootless-only

Rootful Podman does not provide this helper — root already has the privileges needed for host-namespace storage and mount operations:

bash
sudo podman unshare id
output
Error: please use unshare with rootless

Run podman unshare, podman unshare chown, and podman unshare ls as the same unprivileged account that owns the rootless containers.


podman unshare is not available remotely

podman unshare manipulates namespaces on the local Linux host. The remote Podman client cannot enter your machine's user or mount namespaces over SSH. Run unshare on the host where rootless Podman stores containers, or SSH in as the rootless user and execute it there.


Environment variables inside podman unshare

Podman exports storage paths inside the namespace — useful for scripts that must target the correct rootless store:

bash
podman unshare env | grep '^CONTAINERS_'
output
CONTAINERS_GRAPHROOT=/home/podtest/.local/share/containers/storage
CONTAINERS_RUNROOT=/run/user/1014/containers

CONTAINERS_GRAPHROOT is the images-and-layers store; CONTAINERS_RUNROOT is the runtime state directory. Read them for context; do not hand-edit storage internals. Path layout details live in Podman storage location.


podman unshare exit codes

Exit status follows the usual Podman conventions:

  • 125 — Podman command error (invalid subcommand or flag)
  • 126 — command exists but cannot be invoked
  • 127 — command not found inside unshare
  • otherwise — the child command's exit status

A failing child command returns its own code — podman unshare false prints nothing and exits 1.

An unknown executable returns 127:

bash
podman unshare nosuchcmd999
output
Error: exec: "nosuchcmd999": executable file not found in $PATH

Podman itself returns 125 when the wrapped command fails at the Podman layer:

bash
podman unshare podman invalidsub
output
Error: unrecognized command `podman invalidsub`
Try 'podman --help' for more information

When should you use podman unshare?

Problem Useful command
understand rootless UID mapping podman unshare cat /proc/self/uid_map
inspect mapped ownership podman unshare ls -ln
fix mapped ownership podman unshare chown
inspect rootless mounted rootfs podman unshare + podman mount
inspect rootless bridge namespace podman unshare --rootless-netns
fix general SELinux denial diagnose in volume permissions guide, not primarily unshare

References


Summary

podman unshare is how you step into the same user namespace rootless Podman uses. Inside it you appear as namespace UID 0, but you still map to your normal host account — not host root. That is the view you need when a bind mount shows 984038 on the host and 999 inside a container: same inode, different numeric translation.

The highest-value operations are inspection and repair. Read uid_map and gid_map, compare ls -ln from your shell versus podman unshare ls -ln, and fix ownership with podman unshare chown using container-visible IDs instead of hand-calculating subordinate offsets. For filesystem surgery on a stopped container, run podman mount inside unshare — the merged path is not usable from a normal host shell even when the path string looks familiar.

--rootless-netns solves a different problem: reaching bridge-network container IPs that the host namespace cannot ping. Rootful Podman rejects unshare outright with please use unshare with rootless. When SELinux, mode bits, or mount-flag questions dominate, start with the volume permissions guide; use this page when the namespace mapping itself is what you need to see or change.


Frequently Asked Questions

1. What does podman unshare do?

podman unshare runs a command inside the same user namespace rootless Podman uses. Inside that namespace you appear as uid 0, but you still map back to your normal host account — not host root. Use it to inspect uid_map, read mapped file ownership, chown using container-visible IDs, and run podman mount.

2. Why does podman unshare chown use container UIDs like 999?

chown inside podman unshare operates in Podman's user namespace. You pass the ID the container would see, such as 999, and Podman translates it to the correct host subordinate UID. That is easier than calculating subuid start plus container UID by hand.

3. Why cannot I ls the path from podman mount in my normal shell?

Rootless podman mount creates the overlay mount inside Podman's user and mount namespace. The path string exists on disk, but the mount is not visible from your initial host namespace. Run podman mount inside podman unshare, or inspect files in the same unshare invocation.

4. Why does sudo podman unshare fail?

podman unshare is rootless-only. Rootful Podman exits with please use unshare with rootless because there is no rootless mapping for root to enter. Run unshare as the same unprivileged user that runs rootless containers.

5. What is podman unshare --rootless-netns for?

It joins the additional rootless network namespace Netavark uses for user-created bridge networks. Container IPs on that bridge are often unreachable from the host's normal network namespace but respond to ping or curl from inside --rootless-netns.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)