| 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 Podman installed |
| Privilege | Rootful examples as root; rootless examples as an unprivileged user |
| Scope | Where Podman stores images, containers, and volumes — podman info, graphroot, runroot, volumePath, storage.conf precedence, overlay vs fuse-overlayfs, changing graphroot, SELinux relabel on move, imagestore, additionalimagestores, rootless NFS limits, and transient_store. Does not cover disk-full recovery, storage corruption repair, volume tutorials, or SELinux mount permissions. |
| Related guides | Rootless Podman Bind mount vs volume Install Podman on RHEL |
Podman does not scatter images and containers at random paths under /var/lib. It stores everything through the containers/storage library, driven by storage.conf. Do not rely on memorized default paths — ask the Podman installation you are running right now.
Where does Podman store containers and images?
The supported answer is always podman info. It reports the active paths, driver, and configuration file for this host and user.
As root on the lab host:
podman info --format 'graphRoot={{.Store.GraphRoot}} runRoot={{.Store.RunRoot}} volumePath={{.Store.VolumePath}}'Sample output:
graphRoot=/var/lib/containers/storage runRoot=/run/containers/storage volumePath=/var/lib/containers/storage/volumesThose three paths are the practical answer for rootful Podman on this machine. For the full store block in one JSON object:
podman info --format '{{json .Store}}'The graphRoot, runRoot, volumePath, graphDriverName, graphStatus, graphOptions, configFile, and transientStore fields are the ones to read first.
Typical defaults when no override exists:
| Mode | Typical graphroot |
Typical runroot |
Typical volumePath |
|---|---|---|---|
| Rootful | /var/lib/containers/storage |
/run/containers/storage |
/var/lib/containers/storage/volumes |
| Rootless | $HOME/.local/share/containers/storage |
/run/user/UID/containers |
under rootless graphroot/volumes |
Rootless paths follow $XDG_DATA_HOME when set. graphroot and runroot come from containers/storage configuration, while Podman's volumePath normally follows the storage location but can be overridden separately with volume_path in containers.conf. Always confirm the effective paths with podman info.
Understand graphroot, runroot, and volumePath
| Path or setting | Purpose |
|---|---|
graphroot |
Primary persistent read/write container storage |
runroot |
Temporary runtime storage |
volumePath |
Location of Podman-managed volumes |
imagestore |
Optional separate location for newly stored images |
additionalimagestores |
Additional read-only image store paths |
graphroot
graphroot holds persistent containers/storage data:
- image and container layer storage managed by the graph driver
- container writable layer content
- storage metadata databases
These are implementation directories. Manage lifecycle through Podman commands — do not edit overlay databases by hand.
runroot
runroot is temporary writable state for the storage driver at runtime. Rootful Podman on the lab host uses /run/containers/storage; rootless uses a path under /run/user/UID/containers. Data here is not your application's long-term store — it is runtime scratch space that disappears on reboot.
Volume path
Named volumes created by Podman's default volume driver normally live below the storage tree, but their effective location is controlled by Podman's volume_path setting in containers.conf. podman info reports the resulting volumePath, so use that instead of assuming <graphroot>/volumes.
Confirm the active directory:
podman info --format 'volumePath={{.Store.VolumePath}}'volumePath=/var/lib/containers/storage/volumesUse volume names in run commands and Compose files — not hard-coded _data paths.
Find where a specific image is stored
Podman images are not single .tar files on disk. Overlay stores them as layered directories under graphroot.
Inspect the graph driver data for one image:
podman image inspect docker.io/library/alpine:3.20 --format '{{json .GraphDriver}}'Sample output:
{"Name":"overlay","Data":{"UpperDir":"/var/lib/containers/storage/overlay/08bc4e534116aa76b16015484b82eac51f9a593416feae9296c8a2d4bb7aa4a2/diff","WorkDir":"/var/lib/containers/storage/overlay/08bc4e534116aa76b16015484b82eac51f9a593416feae9296c8a2d4bb7aa4a2/work"}}The hash-named directories under /var/lib/containers/storage/overlay/ are storage internals. Understanding that layers live under graphroot is enough — do not rename or delete them manually.
Find where a Podman container is stored
A running container adds a writable upper layer on top of image layers. Create a short-lived container to inspect:
podman run -d --name storage-demo docker.io/library/alpine:3.20 sleep 600Read the overlay paths Podman assigned:
podman inspect storage-demo --format '{{json .GraphDriver}}'Output includes LowerDir, UpperDir, MergedDir, and WorkDir under graphroot:
{"Name":"overlay","Data":{"LowerDir":"/var/lib/containers/storage/overlay/.../diff","MergedDir":"/var/lib/containers/storage/overlay/.../merged","UpperDir":"/var/lib/containers/storage/overlay/.../diff","WorkDir":"/var/lib/containers/storage/overlay/.../work"}}Remove the container with podman rm -f storage-demo when you finish — not by deleting overlay-containers/ directories. Manual deletion can leave storage metadata inconsistent.
Find where a Podman volume is stored
Create a named volume, then ask Podman where it landed:
podman volume create storage-demo-volInspect the volume to read its Mountpoint:
podman volume inspect storage-demo-volRead the Mountpoint field:
[
{
"Name": "storage-demo-vol",
"Driver": "local",
"Mountpoint": "/var/lib/containers/storage/volumes/storage-demo-vol/_data",
...
}
]That path helps you verify backups or disk allocation. Application definitions should still reference the volume name storage-demo-vol, not the _data directory.
Which storage.conf file does Podman use?
Podman reads storage.conf from a precedence chain. The comments in the shipped file list:
/usr/containers/storage.conf
/etc/containers/storage.conf
$HOME/.config/containers/storage.conf
$XDG_CONFIG_HOME/containers/storage.confOn RHEL 10 the vendor file lives at /usr/share/containers/storage.conf. The lab host has no /etc/containers/storage.conf, so Podman uses the vendor file directly:
podman info --format 'configFile={{.Store.ConfigFile}}'configFile=/usr/share/containers/storage.confImportant behavior: the highest-precedence storage.conf that applies replaces the effective configuration. Podman does not inherit individual unset fields from lower-precedence files. If you drop a [storage.options] block in a user override, you lose those options entirely unless you repeat them.
Rootful configuration
System administrators normally add overrides at /etc/containers/storage.conf. That file does not exist on a fresh lab install, which is why podman info points at /usr/share/containers/storage.conf.
Rootless configuration
Rootless Podman reports the user config path even before the file exists:
su - podstore -c 'podman info --format configFile={{.Store.ConfigFile}}'configFile=/home/podstore/.config/containers/storage.confCreate ~/.config/containers/storage.conf to override rootless storage. Do not edit /etc/containers/storage.conf for a single user's rootless store unless you intend a system-wide change.
Understand the overlay storage driver
Confirm the active driver:
podman info --format 'graphDriverName={{.Store.GraphDriverName}}'graphDriverName=overlayOverlayFS presents a merged view to the container:
lower layers → image layers (read-only)
upper layer → container writable changes
merged view → filesystem seen inside the containerRead driver capabilities from podman info:
podman info --format '{{json .Store.GraphStatus}}'Rootful sample output on the lab host:
{"Backing Filesystem": "xfs", "Native Overlay Diff": "false", "Supports d_type": "true", "Supports shifting": "true", "Supports volatile": "true", "Using metacopy": "true"}Backing Filesystem shows the filesystem under graphroot. Native Overlay Diff, Using metacopy, and related flags describe overlay behavior on this kernel — not something you need to tune unless troubleshooting driver performance.
Native rootless OverlayFS vs fuse-overlayfs
Rootless Podman needs a kernel that supports rootless overlay. Kernels before 5.12.9 do not support native rootless OverlayFS. The lab kernel is well above that threshold:
uname -r6.12.0-211.47.1.el10_2.x86_64Compare rootful and rootless overlay behavior:
podman info --format '{{json .Store.GraphStatus}}'Rootful on the lab host reports "Native Overlay Diff": "false" with overlay.mountopt set to nodev,metacopy=on.
As rootless user podstore:
su - podstore -c 'podman info --format "{{json .Store.GraphStatus}}"'{"Backing Filesystem": "xfs", "Native Overlay Diff": "true", "Supports d_type": "true", "Supports shifting": "false", "Supports volatile": "true", "Using metacopy": "false"}Rootless Podman here uses native kernel OverlayFS — no fuse-overlayfs helper.
When fuse-overlayfs still matters
fuse-overlayfs remains relevant when:
- the kernel lacks rootless overlay support
- the backing filesystem cannot use native overlay
- you explicitly set
mount_programinstorage.conf
The default RHEL storage.conf leaves it commented:
#mount_program = "/usr/bin/fuse-overlayfs"Do not install fuse-overlayfs on every modern host. Check graphOptions and graphStatus in podman info first.
Configure fuse-overlayfs when required
Add this only when podman info shows native overlay is unavailable or unsuitable:
[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"Place the snippet in the storage.conf that matches your scope — /etc/containers/storage.conf for rootful, ~/.config/containers/storage.conf for rootless.
If a rootless storage.conf was created on an older kernel, automatic driver selection may differ from a clean install on a current system. After any change, verify with podman info before pulling images.
Change Podman's graphroot
Relocating graphroot is common when /var is tight and a larger partition is mounted at /data. Start from the live values:
podman info --format 'graphRoot={{.Store.GraphRoot}}'Example override in /etc/containers/storage.conf or ~/.config/containers/storage.conf:
[storage]
driver = "overlay"
graphroot = "/data/containers/storage"Changing graphroot alone does not move existing images, containers, or volumes. Podman looks at the new directory — which starts empty unless you migrate content.
Before editing:
- Stop running containers.
- Inventory images and volumes you need to keep.
- Back up important named volumes and export critical images if required.
- Copy or rsync the old
graphrootto the new path, or accept starting fresh at the new location.
A typical rootful migration copies /var/lib/containers/storage to the new path, updates storage.conf, relabels SELinux (next section), and restarts Podman workloads. Test on a non-production host first.
SELinux when moving graphroot
On SELinux-enforcing RHEL-family hosts, a new graphroot needs the same labeling semantics as the default store. The shipped storage.conf documents the pattern:
sudo semanage fcontext -a -e /var/lib/containers/storage /data/containers/storageApply labels to the new tree:
sudo restorecon -R -v /data/containers/storageSkipping relabel after moving graphroot often produces permission failures that look like generic volume errors — see Fix Podman volume permission denied for mount-level diagnosis, but fix labeling here when the root cause is a relocated store.
Store images separately with imagestore
When only image cache size is the problem, imagestore splits new image storage from container writable content:
[storage]
graphroot = "/var/lib/containers/storage"
imagestore = "/mnt/container-images"Rules:
imagestoremust differ fromgraphroot- newly pulled or stored images land in
imagestore - existing images already under
graphrootremain accessible with overlay storage - container writable layers and volumes stay under
graphroot
This suits a layout where a large partition holds image layers and a smaller fast disk holds active container state. It is more precise than moving the entire Podman store when images alone dominate disk use.
After configuring imagestore, confirm paths in podman info. When no separate store is configured, imageStore in JSON output reports an image count rather than a filesystem path.
imagestore vs additionalimagestores
imagestore |
additionalimagestores |
|
|---|---|---|
| Role | writable destination for newly stored images | additional image sources |
| Count | one configured path | list of paths |
| Typical use | split image cache onto another partition | shared read-only image repositories |
| Writable | yes — new images go here | generally read-only |
Example read-only additional store:
[storage.options]
additionalimagestores = [
"/mnt/shared-container-images"
]additionalimagestores must point at an existing containers/storage image store; it is not a directory for saved image tarballs. Populate that store with tooling that understands the containers/storage layout, then expose it read-only to consumers. Images from read-only stores appear alongside local images when you run podman images. Do not treat additionalimagestores as a second writable cache.
Rootless Podman storage on NFS
NFS, Lustre, and GPFS or Spectrum Scale cannot host rootless Podman graphroot because they do not understand user namespaces. A common failure mode is $HOME on NFS, which places the default rootless store at:
$HOME/.local/share/containers/storageFix by pointing rootless storage at local disk in ~/.config/containers/storage.conf:
[storage]
driver = "overlay"
graphroot = "/local/storage/podstore/containers"
runroot = "/run/user/1000/containers"Use a path on a local filesystem the user can write. This addresses Podman's storage backend — not bind-mounting application data from NFS into a container, which is a separate topic.
rootless_storage_path vs per-user graphroot
rootless_storage_path is a storage.conf setting that administrators can use to change the default rootless storage location for users. It is especially useful when home directories are on NFS but local storage is available elsewhere.
A per-user graphroot= in ~/.config/containers/storage.conf can override the effective rootless store for that user. After any change, verify the result with podman info.
Environment variables such as XDG_DATA_HOME shift the default rootless location before any user storage.conf exists. Once you set graphroot explicitly, that setting wins — verify with:
podman info --format 'graphRoot={{.Store.GraphRoot}}'transient_store: what it actually does
transient_store changes where container metadata is kept, not whether all layer files vanish on reboot:
[storage]
transient_store = trueWith transient_store enabled, container metadata uses temporary storage under runroot. That can improve some performance characteristics but metadata is lost on reboot. Persistent layer files may still exist on disk under graphroot, which is why stale storage can accumulate.
Upstream containers/storage documentation recommends leaving transient_store disabled for normal installations. If you enable it and reboot, external cleanup may be required — podman system prune --external removes storage not referenced by current metadata. Do not enable this as a generic performance tweak.
The lab host reports:
podman info --format 'transientStore={{.Store.TransientStore}}'transientStore=falseCheck Podman storage usage
For a quick space summary:
podman system dfSample output:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 25 1 758.1MB 758.1MB (100%)
Containers 1 1 233.6kB 0B (0%)
Local Volumes 10 1 11B 11B (100%)This shows how much space images, containers, and volumes consume. When the filesystem is full and pulls fail with no space left on device, use the dedicated no-space troubleshooting guide — this article covers where data lives, not emergency recovery.
Do not manually delete Podman storage directories
Do not solve cleanup by removing directories such as:
overlay/
overlay-containers/
overlay-images/
volumes/or storage database files under graphroot. Manual deletion can desynchronize Podman's metadata from on-disk layers.
Use podman rm, podman rmi, podman volume rm, and podman system prune for normal lifecycle work. If metadata is already inconsistent, follow the storage corruption reset procedure — not hand-editing overlay/ trees.
Storage configuration quick reference
| Goal | Setting or command |
|---|---|
| see current storage paths | podman info |
| inspect one volume path | podman volume inspect |
| primary persistent store | graphroot |
| runtime or temp store | runroot |
| move new image storage separately | imagestore |
| attach read-only image stores | additionalimagestores |
| custom rootless local store | rootless graphroot in ~/.config/containers/storage.conf |
| use FUSE overlay helper | mount_program under [storage.options.overlay] |
| temporary metadata mode | transient_store |
| check disk consumption | podman system df |
References
- Podman info manual
- containers-storage.conf(5)
- containers/storage — imagestore
- Podman rootless tutorial — file system considerations
Summary
Podman stores images and container layers according to storage.conf, while the default named-volume location is reported as volumePath and can be configured separately through containers.conf. On the lab host, rootful Podman uses graphroot /var/lib/containers/storage and runroot /run/containers/storage from its storage configuration, while Podman reports volumePath /var/lib/containers/storage/volumes for named volumes. Rootless user podstore stores under ~/.local/share/containers/storage with runtime state in /run/user/1016/containers, using native OverlayFS on kernel 6.12 without fuse-overlayfs.
When you need to relocate storage, edit the correct storage.conf for your scope, migrate existing content if you want to keep it, and relabel SELinux on RHEL-family systems. Use imagestore when only the image cache needs a larger partition, and additionalimagestores for read-only shared image content — not as interchangeable writable caches. Rootless graphroot must sit on a local filesystem that supports user namespaces; NFS home directories require a local override.
For volume mount permission errors at an existing path, see Fix Podman volume permission denied. For creating and backing up named volumes, see Podman volumes.

